Codebase list ciftilib / 649638d
New upstream version 1.5.1 Ghislain Antony Vaillant 7 years ago
90 changed file(s) with 22894 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 *.kdev4
1 *.kate-swp
0 language: cpp
1 sudo: false
2
3 cache:
4 - apt
5 - ccache
6
7 addons:
8 apt:
9 packages:
10 - libqt4-dev
11 - libxml++2.6-dev
12 - libboost-dev
13 - libboost-filesystem-dev
14 - zlib1g-dev
15
16 compiler:
17 - clang
18 - gcc
19
20 env:
21 - IGNORE_QT=false SHARED=true
22 - IGNORE_QT=false SHARED=false
23 - IGNORE_QT=true SHARED=true
24 - IGNORE_QT=true SHARED=false
25
26 before_install:
27 - mkdir ../build
28 - cd ../build
29
30 script:
31 - cmake -D BUILD_SHARED_LIBS:BOOL=$SHARED -D IGNORE_QT:BOOL=$IGNORE_QT ../CiftiLib
32 - make -j 4
33 - ctest
0 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
1
2 #UseDoxygen trips over a policy change in cmake 3, suppress the warning
3 CMAKE_POLICY(VERSION 2.8.7)
4 #the suggested version-type policy command doesn't shut this warning up, so set it manually
5 IF (POLICY CMP0045)
6 CMAKE_POLICY(SET CMP0045 OLD)
7 ENDIF (POLICY CMP0045)
8
9 PROJECT(CiftiLib)
10
11 SET(CIFTILIB_VERSION 1.5)
12
13 #MSVC seems like the only compiler that chokes on -W -Wall
14 IF (NOT MSVC)
15 SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall")
16 ENDIF (NOT MSVC)
17
18 SET(IGNORE_QT FALSE CACHE BOOL "don't try to use QT")
19
20 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/" "${CMAKE_SOURCE_DIR}/cmake/Modules/UseDoxygen/")
21
22 INCLUDE(UseDoxygen)
23
24 #QT
25 IF (NOT IGNORE_QT)
26 FIND_PACKAGE(Qt4 4.8.0 QUIET)
27 IF (QT_FOUND)
28 SET(QT_DONT_USE_QTGUI TRUE)
29 ADD_DEFINITIONS(-DCIFTILIB_USE_QT)
30 INCLUDE(${QT_USE_FILE})
31 SET(LIBS ${LIBS} ${QT_LIBRARIES})
32 #for pkg-config file
33 SET(CIFTILIB_PKGCONFIG_REQUIRES_LINE "Requires: QtCore >= 4.8.0 QtXml")
34 SET(CIFTILIB_PKGCONFIG_DEFINE "-DCIFTILIB_USE_QT")
35 ELSE (QT_FOUND)
36 FIND_PACKAGE(Qt5Core)
37 IF (Qt5Core_FOUND)
38 SET(QT_FOUND TRUE)
39 INCLUDE_DIRECTORIES(${Qt5Core_INCLUDE_DIRS})
40 SET(LIBS ${LIBS} Qt5::Core)
41 #whatever that means
42 ADD_DEFINITIONS(-DCIFTILIB_USE_QT)
43 SET(CIFTILIB_PKGCONFIG_REQUIRES_LINE "Requires: Qt5Core Qt5Xml")
44 SET(CIFTILIB_PKGCONFIG_DEFINE "-DCIFTILIB_USE_QT")
45 ENDIF (Qt5Core_FOUND)
46 ENDIF (QT_FOUND)
47 ENDIF (NOT IGNORE_QT)
48
49 #alternative to QT xml, string
50 IF (NOT QT_FOUND)
51 FIND_PACKAGE(libxml++ 2.17.0 REQUIRED)
52 INCLUDE_DIRECTORIES(${libxml++_INCLUDE_DIRS})
53 SET(LIBS ${LIBS} ${libxml++_LIBRARIES})
54 ADD_DEFINITIONS(-DCIFTILIB_USE_XMLPP)
55 #for pkg-config file
56 SET(CIFTILIB_PKGCONFIG_REQUIRES_LINE "Requires: libxml++-2.6 >= 2.17.0")
57 SET(CIFTILIB_PKGCONFIG_DEFINE "-DCIFTILIB_USE_XMLPP")
58 ENDIF (NOT QT_FOUND)
59
60 #boost, including filesystem if not using QT
61 IF (NOT QT_FOUND)
62 FIND_PACKAGE(Boost REQUIRED COMPONENTS filesystem system)
63 ELSE (NOT QT_FOUND)
64 FIND_PACKAGE(Boost REQUIRED)
65 ENDIF (NOT QT_FOUND)
66 INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
67 SET(LIBS ${LIBS} ${Boost_LIBRARIES})
68 #boost quirks
69 IF (Boost_VERSION LESS 104400)
70 #absolute() was added in 1.44.0, with filesystem v3
71 ADD_DEFINITIONS(-DCIFTILIB_BOOST_NO_FSV3)
72 ENDIF (Boost_VERSION LESS 104400)
73 IF (Boost_VERSION LESS 104800)
74 #canonical() was added in 1.48.0
75 ADD_DEFINITIONS(-DCIFTILIB_BOOST_NO_CANONICAL)
76 ENDIF (Boost_VERSION LESS 104800)
77 IF (Boost_VERSION LESS 105600)
78 #try_lexical_cast was added in 1.56.0
79 ADD_DEFINITIONS(-DCIFTILIB_BOOST_NO_TRY_LEXICAL)
80 ENDIF (Boost_VERSION LESS 105600)
81
82 #zlib, useful for volume reading
83 FIND_PACKAGE(ZLIB)
84 IF (ZLIB_FOUND)
85 INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
86 SET(LIBS ${LIBS} ${ZLIB_LIBRARIES})
87 ADD_DEFINITIONS("-DCIFTILIB_HAVE_ZLIB")
88 ENDIF (ZLIB_FOUND)
89 #OS X has some weirdness in its zlib, so let the preprocessor know
90 IF (APPLE)
91 ADD_DEFINITIONS(-DCIFTILIB_OS_MACOSX)
92 ENDIF (APPLE)
93
94 #openmp provides a fast mutex implementation, faster than QT (and probably faster than glibmm)
95 FIND_PACKAGE(OpenMP)
96 IF (OPENMP_FOUND)
97 SET(CMAKE_CXX_FLAGS "${OpenMP_CXX_FLAGS} ${CMAKE_CXX_FLAGS}")
98 ENDIF (OPENMP_FOUND)
99
100 ENABLE_TESTING()
101
102 #the library source, doesn't contain build targets
103 ADD_SUBDIRECTORY(src)
104
105 #example directory, has build targets and tests
106 ADD_SUBDIRECTORY(example)
107
108 macro(append_subdir_files variable dirname)
109 get_directory_property(holder DIRECTORY ${dirname} DEFINITION ${variable})
110 foreach(depfile ${holder})
111 list(APPEND ${variable} "${dirname}/${depfile}")
112 endforeach()
113 endmacro()
114
115 #get the sources and install info
116 append_subdir_files(SOURCES src)
117 append_subdir_files(HEADERS src)
118 append_subdir_files(PUBLIC_HEADERS src)
119 append_subdir_files(PRIVATE_DIRS src)
120
121 ADD_LIBRARY(Cifti
122 ${SOURCES}
123 ${HEADERS}
124 )
125
126 #one way to get qt5's new compiler flag restrictions into the build - does it have other consequences?
127 TARGET_LINK_LIBRARIES(Cifti ${LIBS})
128
129 #NOTE: soversion set to 0 because ABI compatibility was not designed into the interface
130 #soversion defines what symlinks are created, version defines what to put on the end of the actual library file
131 SET_TARGET_PROPERTIES(Cifti
132 PROPERTIES
133 OUTPUT_NAME Cifti
134 SOVERSION 0
135 VERSION ${CIFTILIB_VERSION}
136 )
137
138 INCLUDE_DIRECTORIES(
139 ${CMAKE_SOURCE_DIR}/src
140 )
141
142 #install dirs
143 INCLUDE(GNUInstallDirs)
144
145 #pkg-config
146 CONFIGURE_FILE(CiftiLib.pc.in CiftiLib.pc @ONLY)
147 INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/CiftiLib.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
148
149 #install
150 INSTALL(TARGETS Cifti DESTINATION ${CMAKE_INSTALL_LIBDIR})
151 INSTALL(FILES ${PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CiftiLib)
152 INSTALL(DIRECTORY ${PRIVATE_DIRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CiftiLib FILES_MATCHING PATTERN "*.h")
0 includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
1 libdir=@CMAKE_INSTALL_FULL_LIBDIR@
2
3 Name: CiftiLib
4 Description: C++ Library for reading and writing CIFTI-2 and CIFTI-1 files
5 Version: @CIFTILIB_VERSION@
6 URL: https://github.com/Washington-University/CiftiLib
7 Cflags: -I${includedir}/CiftiLib @CIFTILIB_PKGCONFIG_DEFINE@
8 Libs: -L${libdir} -lCifti
9 @CIFTILIB_PKGCONFIG_REQUIRES_LINE@
0 # Doxyfile 1.5.8
1
2 # This file describes the settings to be used by the documentation system
3 # doxygen (www.doxygen.org) for a project
4 #
5 # All text after a hash (#) is considered a comment and will be ignored
6 # The format is:
7 # TAG = value [value, ...]
8 # For lists items can also be appended using:
9 # TAG += value [value, ...]
10 # Values that contain spaces should be placed between quotes (" ")
11
12 #---------------------------------------------------------------------------
13 # Project related configuration options
14 #---------------------------------------------------------------------------
15
16 # This tag specifies the encoding used for all characters in the config file
17 # that follow. The default is UTF-8 which is also the encoding used for all
18 # text before the first occurrence of this tag. Doxygen uses libiconv (or the
19 # iconv built into libc) for the transcoding. See
20 # http://www.gnu.org/software/libiconv for the list of possible encodings.
21
22 DOXYFILE_ENCODING = UTF-8
23
24 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded
25 # by quotes) that should identify the project.
26
27 PROJECT_NAME = "@PROJECT_NAME@"
28
29 # The PROJECT_NUMBER tag can be used to enter a project or revision number.
30 # This could be handy for archiving the generated documentation or
31 # if some version control system is used.
32
33 PROJECT_NUMBER = "@PROJECT_VERSION@"
34
35 # Using the PROJECT_BRIEF tag one can provide an optional one line description
36 # for a project that appears at the top of each page and should give viewer
37 # a quick idea about the purpose of the project. Keep the description short.
38
39 PROJECT_BRIEF = "A C++ library for CIFTI-2 and CIFTI-1 files"
40
41 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
42 # base path where the generated documentation will be put.
43 # If a relative path is entered, it will be relative to the location
44 # where doxygen was started. If left blank the current directory will be used.
45
46 OUTPUT_DIRECTORY = "@DOXYFILE_OUTPUT_DIR@"
47
48 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
49 # 4096 sub-directories (in 2 levels) under the output directory of each output
50 # format and will distribute the generated files over these directories.
51 # Enabling this option can be useful when feeding doxygen a huge amount of
52 # source files, where putting all generated files in the same directory would
53 # otherwise cause performance problems for the file system.
54
55 CREATE_SUBDIRS = NO
56
57 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
58 # documentation generated by doxygen is written. Doxygen will use this
59 # information to generate all constant output in the proper language.
60 # The default language is English, other supported languages are:
61 # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
62 # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
63 # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
64 # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
65 # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
66 # Spanish, Swedish, and Ukrainian.
67
68 OUTPUT_LANGUAGE = English
69
70 # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
71 # include brief member descriptions after the members that are listed in
72 # the file and class documentation (similar to JavaDoc).
73 # Set to NO to disable this.
74
75 BRIEF_MEMBER_DESC = YES
76
77 # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
78 # the brief description of a member or function before the detailed description.
79 # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
80 # brief descriptions will be completely suppressed.
81
82 REPEAT_BRIEF = YES
83
84 # This tag implements a quasi-intelligent brief description abbreviator
85 # that is used to form the text in various listings. Each string
86 # in this list, if found as the leading text of the brief description, will be
87 # stripped from the text and the result after processing the whole list, is
88 # used as the annotated text. Otherwise, the brief description is used as-is.
89 # If left blank, the following values are used ("$name" is automatically
90 # replaced with the name of the entity): "The $name class" "The $name widget"
91 # "The $name file" "is" "provides" "specifies" "contains"
92 # "represents" "a" "an" "the"
93
94 ABBREVIATE_BRIEF =
95
96 # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
97 # Doxygen will generate a detailed section even if there is only a brief
98 # description.
99
100 ALWAYS_DETAILED_SEC = NO
101
102 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
103 # inherited members of a class in the documentation of that class as if those
104 # members were ordinary class members. Constructors, destructors and assignment
105 # operators of the base classes will not be shown.
106
107 INLINE_INHERITED_MEMB = NO
108
109 # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
110 # path before files name in the file list and in the header files. If set
111 # to NO the shortest path that makes the file name unique will be used.
112
113 FULL_PATH_NAMES = NO
114
115 # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
116 # can be used to strip a user-defined part of the path. Stripping is
117 # only done if one of the specified strings matches the left-hand part of
118 # the path. The tag can be used to show relative paths in the file list.
119 # If left blank the directory from which doxygen is run is used as the
120 # path to strip.
121
122 STRIP_FROM_PATH =
123
124 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
125 # the path mentioned in the documentation of a class, which tells
126 # the reader which header file to include in order to use a class.
127 # If left blank only the name of the header file containing the class
128 # definition is used. Otherwise one should specify the include paths that
129 # are normally passed to the compiler using the -I flag.
130
131 STRIP_FROM_INC_PATH =
132
133 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
134 # (but less readable) file names. This can be useful is your file systems
135 # doesn't support long names like on DOS, Mac, or CD-ROM.
136
137 SHORT_NAMES = NO
138
139 # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
140 # will interpret the first line (until the first dot) of a JavaDoc-style
141 # comment as the brief description. If set to NO, the JavaDoc
142 # comments will behave just like regular Qt-style comments
143 # (thus requiring an explicit @brief command for a brief description.)
144
145 JAVADOC_AUTOBRIEF = NO
146
147 # If the QT_AUTOBRIEF tag is set to YES then Doxygen will
148 # interpret the first line (until the first dot) of a Qt-style
149 # comment as the brief description. If set to NO, the comments
150 # will behave just like regular Qt-style comments (thus requiring
151 # an explicit \brief command for a brief description.)
152
153 QT_AUTOBRIEF = NO
154
155 # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
156 # treat a multi-line C++ special comment block (i.e. a block of //! or ///
157 # comments) as a brief description. This used to be the default behaviour.
158 # The new default is to treat a multi-line C++ comment block as a detailed
159 # description. Set this tag to YES if you prefer the old behaviour instead.
160
161 MULTILINE_CPP_IS_BRIEF = NO
162
163 # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
164 # member inherits the documentation from any documented member that it
165 # re-implements.
166
167 INHERIT_DOCS = YES
168
169 # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
170 # a new page for each member. If set to NO, the documentation of a member will
171 # be part of the file/class/namespace that contains it.
172
173 SEPARATE_MEMBER_PAGES = NO
174
175 # The TAB_SIZE tag can be used to set the number of spaces in a tab.
176 # Doxygen uses this value to replace tabs by spaces in code fragments.
177
178 TAB_SIZE = 8
179
180 # This tag can be used to specify a number of aliases that acts
181 # as commands in the documentation. An alias has the form "name=value".
182 # For example adding "sideeffect=\par Side Effects:\n" will allow you to
183 # put the command \sideeffect (or @sideeffect) in the documentation, which
184 # will result in a user-defined paragraph with heading "Side Effects:".
185 # You can put \n's in the value part of an alias to insert newlines.
186
187 ALIASES =
188
189 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
190 # sources only. Doxygen will then generate output that is more tailored for C.
191 # For instance, some of the names that are used will be different. The list
192 # of all members will be omitted, etc.
193
194 OPTIMIZE_OUTPUT_FOR_C = NO
195
196 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
197 # sources only. Doxygen will then generate output that is more tailored for
198 # Java. For instance, namespaces will be presented as packages, qualified
199 # scopes will look different, etc.
200
201 OPTIMIZE_OUTPUT_JAVA = NO
202
203 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
204 # sources only. Doxygen will then generate output that is more tailored for
205 # Fortran.
206
207 OPTIMIZE_FOR_FORTRAN = NO
208
209 # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
210 # sources. Doxygen will then generate output that is tailored for
211 # VHDL.
212
213 OPTIMIZE_OUTPUT_VHDL = NO
214
215 # Doxygen selects the parser to use depending on the extension of the files it parses.
216 # With this tag you can assign which parser to use for a given extension.
217 # Doxygen has a built-in mapping, but you can override or extend it using this tag.
218 # The format is ext=language, where ext is a file extension, and language is one of
219 # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
220 # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
221 # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
222 # use: inc=Fortran f=C
223
224 EXTENSION_MAPPING =
225
226 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
227 # to include (a tag file for) the STL sources as input, then you should
228 # set this tag to YES in order to let doxygen match functions declarations and
229 # definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
230 # func(std::string) {}). This also make the inheritance and collaboration
231 # diagrams that involve STL classes more complete and accurate.
232
233 BUILTIN_STL_SUPPORT = NO
234
235 # If you use Microsoft's C++/CLI language, you should set this option to YES to
236 # enable parsing support.
237
238 CPP_CLI_SUPPORT = NO
239
240 # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
241 # Doxygen will parse them like normal C++ but will assume all classes use public
242 # instead of private inheritance when no explicit protection keyword is present.
243
244 SIP_SUPPORT = NO
245
246 # For Microsoft's IDL there are propget and propput attributes to indicate getter
247 # and setter methods for a property. Setting this option to YES (the default)
248 # will make doxygen to replace the get and set methods by a property in the
249 # documentation. This will only work if the methods are indeed getting or
250 # setting a simple type. If this is not the case, or you want to show the
251 # methods anyway, you should set this option to NO.
252
253 IDL_PROPERTY_SUPPORT = YES
254
255 # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
256 # tag is set to YES, then doxygen will reuse the documentation of the first
257 # member in the group (if any) for the other members of the group. By default
258 # all members of a group must be documented explicitly.
259
260 DISTRIBUTE_GROUP_DOC = NO
261
262 # Set the SUBGROUPING tag to YES (the default) to allow class member groups of
263 # the same type (for instance a group of public functions) to be put as a
264 # subgroup of that type (e.g. under the Public Functions section). Set it to
265 # NO to prevent subgrouping. Alternatively, this can be done per class using
266 # the \nosubgrouping command.
267
268 SUBGROUPING = YES
269
270 # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
271 # is documented as struct, union, or enum with the name of the typedef. So
272 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
273 # with name TypeT. When disabled the typedef will appear as a member of a file,
274 # namespace, or class. And the struct will be named TypeS. This can typically
275 # be useful for C code in case the coding convention dictates that all compound
276 # types are typedef'ed and only the typedef is referenced, never the tag name.
277
278 TYPEDEF_HIDES_STRUCT = NO
279
280 # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
281 # determine which symbols to keep in memory and which to flush to disk.
282 # When the cache is full, less often used symbols will be written to disk.
283 # For small to medium size projects (<1000 input files) the default value is
284 # probably good enough. For larger projects a too small cache size can cause
285 # doxygen to be busy swapping symbols to and from disk most of the time
286 # causing a significant performance penality.
287 # If the system has enough physical memory increasing the cache will improve the
288 # performance by keeping more symbols in memory. Note that the value works on
289 # a logarithmic scale so increasing the size by one will rougly double the
290 # memory usage. The cache size is given by this formula:
291 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
292 # corresponding to a cache size of 2^16 = 65536 symbols
293
294 SYMBOL_CACHE_SIZE = 0
295
296 #---------------------------------------------------------------------------
297 # Build related configuration options
298 #---------------------------------------------------------------------------
299
300 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
301 # documentation are documented, even if no documentation was available.
302 # Private class members and static file members will be hidden unless
303 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
304
305 EXTRACT_ALL = NO
306
307 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class
308 # will be included in the documentation.
309
310 EXTRACT_PRIVATE = NO
311
312 # If the EXTRACT_STATIC tag is set to YES all static members of a file
313 # will be included in the documentation.
314
315 EXTRACT_STATIC = NO
316
317 # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
318 # defined locally in source files will be included in the documentation.
319 # If set to NO only classes defined in header files are included.
320
321 EXTRACT_LOCAL_CLASSES = YES
322
323 # This flag is only useful for Objective-C code. When set to YES local
324 # methods, which are defined in the implementation section but not in
325 # the interface are included in the documentation.
326 # If set to NO (the default) only methods in the interface are included.
327
328 EXTRACT_LOCAL_METHODS = NO
329
330 # If this flag is set to YES, the members of anonymous namespaces will be
331 # extracted and appear in the documentation as a namespace called
332 # 'anonymous_namespace{file}', where file will be replaced with the base
333 # name of the file that contains the anonymous namespace. By default
334 # anonymous namespace are hidden.
335
336 EXTRACT_ANON_NSPACES = NO
337
338 # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
339 # undocumented members of documented classes, files or namespaces.
340 # If set to NO (the default) these members will be included in the
341 # various overviews, but no documentation section is generated.
342 # This option has no effect if EXTRACT_ALL is enabled.
343
344 HIDE_UNDOC_MEMBERS = NO
345
346 # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
347 # undocumented classes that are normally visible in the class hierarchy.
348 # If set to NO (the default) these classes will be included in the various
349 # overviews. This option has no effect if EXTRACT_ALL is enabled.
350
351 HIDE_UNDOC_CLASSES = NO
352
353 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
354 # friend (class|struct|union) declarations.
355 # If set to NO (the default) these declarations will be included in the
356 # documentation.
357
358 HIDE_FRIEND_COMPOUNDS = NO
359
360 # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
361 # documentation blocks found inside the body of a function.
362 # If set to NO (the default) these blocks will be appended to the
363 # function's detailed documentation block.
364
365 HIDE_IN_BODY_DOCS = NO
366
367 # The INTERNAL_DOCS tag determines if documentation
368 # that is typed after a \internal command is included. If the tag is set
369 # to NO (the default) then the documentation will be excluded.
370 # Set it to YES to include the internal documentation.
371
372 INTERNAL_DOCS = NO
373
374 # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
375 # file names in lower-case letters. If set to YES upper-case letters are also
376 # allowed. This is useful if you have classes or files whose names only differ
377 # in case and if your file system supports case sensitive file names. Windows
378 # and Mac users are advised to set this option to NO.
379
380 CASE_SENSE_NAMES = YES
381
382 # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
383 # will show members with their full class and namespace scopes in the
384 # documentation. If set to YES the scope will be hidden.
385
386 HIDE_SCOPE_NAMES = NO
387
388 # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
389 # will put a list of the files that are included by a file in the documentation
390 # of that file.
391
392 SHOW_INCLUDE_FILES = YES
393
394 # If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
395 # is inserted in the documentation for inline members.
396
397 INLINE_INFO = YES
398
399 # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
400 # will sort the (detailed) documentation of file and class members
401 # alphabetically by member name. If set to NO the members will appear in
402 # declaration order.
403
404 SORT_MEMBER_DOCS = YES
405
406 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
407 # brief documentation of file, namespace and class members alphabetically
408 # by member name. If set to NO (the default) the members will appear in
409 # declaration order.
410
411 SORT_BRIEF_DOCS = NO
412
413 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
414 # hierarchy of group names into alphabetical order. If set to NO (the default)
415 # the group names will appear in their defined order.
416
417 SORT_GROUP_NAMES = NO
418
419 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
420 # sorted by fully-qualified names, including namespaces. If set to
421 # NO (the default), the class list will be sorted only by class name,
422 # not including the namespace part.
423 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
424 # Note: This option applies only to the class list, not to the
425 # alphabetical list.
426
427 SORT_BY_SCOPE_NAME = NO
428
429 # The GENERATE_TODOLIST tag can be used to enable (YES) or
430 # disable (NO) the todo list. This list is created by putting \todo
431 # commands in the documentation.
432
433 GENERATE_TODOLIST = YES
434
435 # The GENERATE_TESTLIST tag can be used to enable (YES) or
436 # disable (NO) the test list. This list is created by putting \test
437 # commands in the documentation.
438
439 GENERATE_TESTLIST = YES
440
441 # The GENERATE_BUGLIST tag can be used to enable (YES) or
442 # disable (NO) the bug list. This list is created by putting \bug
443 # commands in the documentation.
444
445 GENERATE_BUGLIST = YES
446
447 # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
448 # disable (NO) the deprecated list. This list is created by putting
449 # \deprecated commands in the documentation.
450
451 GENERATE_DEPRECATEDLIST= YES
452
453 # The ENABLED_SECTIONS tag can be used to enable conditional
454 # documentation sections, marked by \if sectionname ... \endif.
455
456 ENABLED_SECTIONS =
457
458 # The MAX_INITIALIZER_LINES tag determines the maximum number of lines
459 # the initial value of a variable or define consists of for it to appear in
460 # the documentation. If the initializer consists of more lines than specified
461 # here it will be hidden. Use a value of 0 to hide initializers completely.
462 # The appearance of the initializer of individual variables and defines in the
463 # documentation can be controlled using \showinitializer or \hideinitializer
464 # command in the documentation regardless of this setting.
465
466 MAX_INITIALIZER_LINES = 30
467
468 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated
469 # at the bottom of the documentation of classes and structs. If set to YES the
470 # list will mention the files that were used to generate the documentation.
471
472 SHOW_USED_FILES = YES
473
474 # If the sources in your project are distributed over multiple directories
475 # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
476 # in the documentation. The default is NO.
477
478 SHOW_DIRECTORIES = NO
479
480 # Set the SHOW_FILES tag to NO to disable the generation of the Files page.
481 # This will remove the Files entry from the Quick Index and from the
482 # Folder Tree View (if specified). The default is YES.
483
484 SHOW_FILES = YES
485
486 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the
487 # Namespaces page.
488 # This will remove the Namespaces entry from the Quick Index
489 # and from the Folder Tree View (if specified). The default is YES.
490
491 SHOW_NAMESPACES = YES
492
493 # The FILE_VERSION_FILTER tag can be used to specify a program or script that
494 # doxygen should invoke to get the current version for each file (typically from
495 # the version control system). Doxygen will invoke the program by executing (via
496 # popen()) the command <command> <input-file>, where <command> is the value of
497 # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
498 # provided by doxygen. Whatever the program writes to standard output
499 # is used as the file version. See the manual for examples.
500
501 FILE_VERSION_FILTER =
502
503 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
504 # doxygen. The layout file controls the global structure of the generated output files
505 # in an output format independent way. The create the layout file that represents
506 # doxygen's defaults, run doxygen with the -l option. You can optionally specify a
507 # file name after the option, if omitted DoxygenLayout.xml will be used as the name
508 # of the layout file.
509
510 LAYOUT_FILE =
511
512 #---------------------------------------------------------------------------
513 # configuration options related to warning and progress messages
514 #---------------------------------------------------------------------------
515
516 # The QUIET tag can be used to turn on/off the messages that are generated
517 # by doxygen. Possible values are YES and NO. If left blank NO is used.
518
519 QUIET = YES
520
521 # The WARNINGS tag can be used to turn on/off the warning messages that are
522 # generated by doxygen. Possible values are YES and NO. If left blank
523 # NO is used.
524
525 WARNINGS = YES
526
527 # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
528 # for undocumented members. If EXTRACT_ALL is set to YES then this flag will
529 # automatically be disabled.
530
531 WARN_IF_UNDOCUMENTED = YES
532
533 # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
534 # potential errors in the documentation, such as not documenting some
535 # parameters in a documented function, or documenting parameters that
536 # don't exist or using markup commands wrongly.
537
538 WARN_IF_DOC_ERROR = YES
539
540 # This WARN_NO_PARAMDOC option can be abled to get warnings for
541 # functions that are documented, but have no documentation for their parameters
542 # or return value. If set to NO (the default) doxygen will only warn about
543 # wrong or incomplete parameter documentation, but not about the absence of
544 # documentation.
545
546 WARN_NO_PARAMDOC = NO
547
548 # The WARN_FORMAT tag determines the format of the warning messages that
549 # doxygen can produce. The string should contain the $file, $line, and $text
550 # tags, which will be replaced by the file and line number from which the
551 # warning originated and the warning text. Optionally the format may contain
552 # $version, which will be replaced by the version of the file (if it could
553 # be obtained via FILE_VERSION_FILTER)
554
555 WARN_FORMAT = "$file:$line: $text"
556
557 # The WARN_LOGFILE tag can be used to specify a file to which warning
558 # and error messages should be written. If left blank the output is written
559 # to stderr.
560
561 WARN_LOGFILE =
562
563 #---------------------------------------------------------------------------
564 # configuration options related to the input files
565 #---------------------------------------------------------------------------
566
567 # The INPUT tag can be used to specify the files and/or directories that contain
568 # documented source files. You may enter file names like "myfile.cpp" or
569 # directories like "/usr/src/myproject". Separate the files or directories
570 # with spaces.
571
572 INPUT = @DOXYFILE_SOURCE_DIRS@
573
574 # This tag can be used to specify the character encoding of the source files
575 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
576 # also the default input encoding. Doxygen uses libiconv (or the iconv built
577 # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
578 # the list of possible encodings.
579
580 INPUT_ENCODING = UTF-8
581
582 # If the value of the INPUT tag contains directories, you can use the
583 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
584 # and *.h) to filter out the source-files in the directories. If left
585 # blank the following patterns are tested:
586 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
587 # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
588
589 FILE_PATTERNS =
590
591 # The RECURSIVE tag can be used to turn specify whether or not subdirectories
592 # should be searched for input files as well. Possible values are YES and NO.
593 # If left blank NO is used.
594
595 RECURSIVE = YES
596
597 # The EXCLUDE tag can be used to specify files and/or directories that should
598 # excluded from the INPUT source files. This way you can easily exclude a
599 # subdirectory from a directory tree whose root is specified with the INPUT tag.
600
601 EXCLUDE = "_darcs"
602
603 # The EXCLUDE_SYMLINKS tag can be used select whether or not files or
604 # directories that are symbolic links (a Unix filesystem feature) are excluded
605 # from the input.
606
607 EXCLUDE_SYMLINKS = NO
608
609 # If the value of the INPUT tag contains directories, you can use the
610 # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
611 # certain files from those directories. Note that the wildcards are matched
612 # against the file with absolute path, so to exclude all test directories
613 # for example use the pattern */test/*
614
615 EXCLUDE_PATTERNS = "*/.*" "*/.*/*"
616
617 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
618 # (namespaces, classes, functions, etc.) that should be excluded from the
619 # output. The symbol name can be a fully qualified name, a word, or if the
620 # wildcard * is used, a substring. Examples: ANamespace, AClass,
621 # AClass::ANamespace, ANamespace::*Test
622
623 EXCLUDE_SYMBOLS =
624
625 # The EXAMPLE_PATH tag can be used to specify one or more files or
626 # directories that contain example code fragments that are included (see
627 # the \include command).
628
629 EXAMPLE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/example"
630
631 # If the value of the EXAMPLE_PATH tag contains directories, you can use the
632 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
633 # and *.h) to filter out the source-files in the directories. If left
634 # blank all files are included.
635
636 EXAMPLE_PATTERNS =
637
638 # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
639 # searched for input files to be used with the \include or \dontinclude
640 # commands irrespective of the value of the RECURSIVE tag.
641 # Possible values are YES and NO. If left blank NO is used.
642
643 EXAMPLE_RECURSIVE = NO
644
645 # The IMAGE_PATH tag can be used to specify one or more files or
646 # directories that contain image that are included in the documentation (see
647 # the \image command).
648
649 IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@"
650
651 # The INPUT_FILTER tag can be used to specify a program that doxygen should
652 # invoke to filter for each input file. Doxygen will invoke the filter program
653 # by executing (via popen()) the command <filter> <input-file>, where <filter>
654 # is the value of the INPUT_FILTER tag, and <input-file> is the name of an
655 # input file. Doxygen will then use the output that the filter program writes
656 # to standard output.
657 # If FILTER_PATTERNS is specified, this tag will be
658 # ignored.
659
660 INPUT_FILTER =
661
662 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
663 # basis.
664 # Doxygen will compare the file name with each pattern and apply the
665 # filter if there is a match.
666 # The filters are a list of the form:
667 # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
668 # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
669 # is applied to all files.
670
671 FILTER_PATTERNS =
672
673 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
674 # INPUT_FILTER) will be used to filter the input files when producing source
675 # files to browse (i.e. when SOURCE_BROWSER is set to YES).
676
677 FILTER_SOURCE_FILES = NO
678
679 #---------------------------------------------------------------------------
680 # configuration options related to source browsing
681 #---------------------------------------------------------------------------
682
683 # If the SOURCE_BROWSER tag is set to YES then a list of source files will
684 # be generated. Documented entities will be cross-referenced with these sources.
685 # Note: To get rid of all source code in the generated output, make sure also
686 # VERBATIM_HEADERS is set to NO.
687
688 SOURCE_BROWSER = NO
689
690 # Setting the INLINE_SOURCES tag to YES will include the body
691 # of functions and classes directly in the documentation.
692
693 INLINE_SOURCES = NO
694
695 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
696 # doxygen to hide any special comment blocks from generated source code
697 # fragments. Normal C and C++ comments will always remain visible.
698
699 STRIP_CODE_COMMENTS = YES
700
701 # If the REFERENCED_BY_RELATION tag is set to YES
702 # then for each documented function all documented
703 # functions referencing it will be listed.
704
705 REFERENCED_BY_RELATION = NO
706
707 # If the REFERENCES_RELATION tag is set to YES
708 # then for each documented function all documented entities
709 # called/used by that function will be listed.
710
711 REFERENCES_RELATION = NO
712
713 # If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
714 # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
715 # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
716 # link to the source code.
717 # Otherwise they will link to the documentation.
718
719 REFERENCES_LINK_SOURCE = YES
720
721 # If the USE_HTAGS tag is set to YES then the references to source code
722 # will point to the HTML generated by the htags(1) tool instead of doxygen
723 # built-in source browser. The htags tool is part of GNU's global source
724 # tagging system (see http://www.gnu.org/software/global/global.html). You
725 # will need version 4.8.6 or higher.
726
727 USE_HTAGS = NO
728
729 # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
730 # will generate a verbatim copy of the header file for each class for
731 # which an include is specified. Set to NO to disable this.
732
733 VERBATIM_HEADERS = YES
734
735 #---------------------------------------------------------------------------
736 # configuration options related to the alphabetical class index
737 #---------------------------------------------------------------------------
738
739 # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
740 # of all compounds will be generated. Enable this if the project
741 # contains a lot of classes, structs, unions or interfaces.
742
743 ALPHABETICAL_INDEX = NO
744
745 # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
746 # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
747 # in which this list will be split (can be a number in the range [1..20])
748
749 COLS_IN_ALPHA_INDEX = 5
750
751 # In case all classes in a project start with a common prefix, all
752 # classes will be put under the same header in the alphabetical index.
753 # The IGNORE_PREFIX tag can be used to specify one or more prefixes that
754 # should be ignored while generating the index headers.
755
756 IGNORE_PREFIX =
757
758 #---------------------------------------------------------------------------
759 # configuration options related to the HTML output
760 #---------------------------------------------------------------------------
761
762 # If the GENERATE_HTML tag is set to YES (the default) Doxygen will
763 # generate HTML output.
764
765 GENERATE_HTML = YES
766
767 # The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
768 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
769 # put in front of it. If left blank `html' will be used as the default path.
770
771 HTML_OUTPUT = "@DOXYFILE_HTML_DIR@"
772
773 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for
774 # each generated HTML page (for example: .htm,.php,.asp). If it is left blank
775 # doxygen will generate files with .html extension.
776
777 HTML_FILE_EXTENSION = .html
778
779 # The HTML_HEADER tag can be used to specify a personal HTML header for
780 # each generated HTML page. If it is left blank doxygen will generate a
781 # standard header.
782
783 HTML_HEADER =
784
785 # The HTML_FOOTER tag can be used to specify a personal HTML footer for
786 # each generated HTML page. If it is left blank doxygen will generate a
787 # standard footer.
788
789 HTML_FOOTER =
790
791 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
792 # style sheet that is used by each HTML page. It can be used to
793 # fine-tune the look of the HTML output. If the tag is left blank doxygen
794 # will generate a default style sheet. Note that doxygen will try to copy
795 # the style sheet file to the HTML output directory, so don't put your own
796 # stylesheet in the HTML output directory as well, or it will be erased!
797
798 HTML_STYLESHEET =
799
800 # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
801 # files or namespaces will be aligned in HTML using tables. If set to
802 # NO a bullet list will be used.
803
804 HTML_ALIGN_MEMBERS = YES
805
806 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
807 # documentation will contain sections that can be hidden and shown after the
808 # page has loaded. For this to work a browser that supports
809 # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
810 # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
811
812 HTML_DYNAMIC_SECTIONS = NO
813
814 # If the GENERATE_DOCSET tag is set to YES, additional index files
815 # will be generated that can be used as input for Apple's Xcode 3
816 # integrated development environment, introduced with OSX 10.5 (Leopard).
817 # To create a documentation set, doxygen will generate a Makefile in the
818 # HTML output directory. Running make will produce the docset in that
819 # directory and running "make install" will install the docset in
820 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
821 # it at startup.
822 # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
823
824 GENERATE_DOCSET = NO
825
826 # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
827 # feed. A documentation feed provides an umbrella under which multiple
828 # documentation sets from a single provider (such as a company or product suite)
829 # can be grouped.
830
831 DOCSET_FEEDNAME = "Doxygen generated docs"
832
833 # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
834 # should uniquely identify the documentation set bundle. This should be a
835 # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
836 # will append .docset to the name.
837
838 DOCSET_BUNDLE_ID = org.doxygen.Project
839
840 # If the GENERATE_HTMLHELP tag is set to YES, additional index files
841 # will be generated that can be used as input for tools like the
842 # Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
843 # of the generated HTML documentation.
844
845 GENERATE_HTMLHELP = NO
846
847 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
848 # be used to specify the file name of the resulting .chm file. You
849 # can add a path in front of the file if the result should not be
850 # written to the html output directory.
851
852 CHM_FILE =
853
854 # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
855 # be used to specify the location (absolute path including file name) of
856 # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
857 # the HTML help compiler on the generated index.hhp.
858
859 HHC_LOCATION =
860
861 # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
862 # controls if a separate .chi index file is generated (YES) or that
863 # it should be included in the master .chm file (NO).
864
865 GENERATE_CHI = NO
866
867 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
868 # is used to encode HtmlHelp index (hhk), content (hhc) and project file
869 # content.
870
871 CHM_INDEX_ENCODING =
872
873 # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
874 # controls whether a binary table of contents is generated (YES) or a
875 # normal table of contents (NO) in the .chm file.
876
877 BINARY_TOC = NO
878
879 # The TOC_EXPAND flag can be set to YES to add extra items for group members
880 # to the contents of the HTML help documentation and to the tree view.
881
882 TOC_EXPAND = NO
883
884 # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
885 # are set, an additional index file will be generated that can be used as input for
886 # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
887 # HTML documentation.
888
889 GENERATE_QHP = NO
890
891 # If the QHG_LOCATION tag is specified, the QCH_FILE tag can
892 # be used to specify the file name of the resulting .qch file.
893 # The path specified is relative to the HTML output folder.
894
895 QCH_FILE =
896
897 # The QHP_NAMESPACE tag specifies the namespace to use when generating
898 # Qt Help Project output. For more information please see
899 # http://doc.trolltech.com/qthelpproject.html#namespace
900
901 QHP_NAMESPACE =
902
903 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
904 # Qt Help Project output. For more information please see
905 # http://doc.trolltech.com/qthelpproject.html#virtual-folders
906
907 QHP_VIRTUAL_FOLDER = doc
908
909 # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
910 # For more information please see
911 # http://doc.trolltech.com/qthelpproject.html#custom-filters
912
913 QHP_CUST_FILTER_NAME =
914
915 # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
916 # <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
917
918 QHP_CUST_FILTER_ATTRS =
919
920 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
921 # filter section matches.
922 # <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
923
924 QHP_SECT_FILTER_ATTRS =
925
926 # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
927 # be used to specify the location of Qt's qhelpgenerator.
928 # If non-empty doxygen will try to run qhelpgenerator on the generated
929 # .qhp file.
930
931 QHG_LOCATION =
932
933 # The DISABLE_INDEX tag can be used to turn on/off the condensed index at
934 # top of each HTML page. The value NO (the default) enables the index and
935 # the value YES disables it.
936
937 DISABLE_INDEX = NO
938
939 # This tag can be used to set the number of enum values (range [1..20])
940 # that doxygen will group on one line in the generated HTML documentation.
941
942 ENUM_VALUES_PER_LINE = 4
943
944 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
945 # structure should be generated to display hierarchical information.
946 # If the tag value is set to FRAME, a side panel will be generated
947 # containing a tree-like index structure (just like the one that
948 # is generated for HTML Help). For this to work a browser that supports
949 # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
950 # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
951 # probably better off using the HTML help feature. Other possible values
952 # for this tag are: HIERARCHIES, which will generate the Groups, Directories,
953 # and Class Hierarchy pages using a tree view instead of an ordered list;
954 # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
955 # disables this behavior completely. For backwards compatibility with previous
956 # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
957 # respectively.
958
959 GENERATE_TREEVIEW = NONE
960
961 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
962 # used to set the initial width (in pixels) of the frame in which the tree
963 # is shown.
964
965 TREEVIEW_WIDTH = 250
966
967 # Use this tag to change the font size of Latex formulas included
968 # as images in the HTML documentation. The default is 10. Note that
969 # when you change the font size after a successful doxygen run you need
970 # to manually remove any form_*.png images from the HTML output directory
971 # to force them to be regenerated.
972
973 FORMULA_FONTSIZE = 10
974
975 #---------------------------------------------------------------------------
976 # configuration options related to the LaTeX output
977 #---------------------------------------------------------------------------
978
979 # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
980 # generate Latex output.
981
982 GENERATE_LATEX = @DOXYFILE_GENERATE_LATEX@
983
984 # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
985 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
986 # put in front of it. If left blank `latex' will be used as the default path.
987
988 LATEX_OUTPUT = "@DOXYFILE_LATEX_DIR@"
989
990 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
991 # invoked. If left blank `latex' will be used as the default command name.
992
993 LATEX_CMD_NAME = "@LATEX_COMPILER@"
994
995 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
996 # generate index for LaTeX. If left blank `makeindex' will be used as the
997 # default command name.
998
999 MAKEINDEX_CMD_NAME = "@MAKEINDEX_COMPILER@"
1000
1001 # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
1002 # LaTeX documents. This may be useful for small projects and may help to
1003 # save some trees in general.
1004
1005 COMPACT_LATEX = NO
1006
1007 # The PAPER_TYPE tag can be used to set the paper type that is used
1008 # by the printer. Possible values are: a4, a4wide, letter, legal and
1009 # executive. If left blank a4wide will be used.
1010
1011 PAPER_TYPE = a4wide
1012
1013 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
1014 # packages that should be included in the LaTeX output.
1015
1016 EXTRA_PACKAGES =
1017
1018 # The LATEX_HEADER tag can be used to specify a personal LaTeX header for
1019 # the generated latex document. The header should contain everything until
1020 # the first chapter. If it is left blank doxygen will generate a
1021 # standard header. Notice: only use this tag if you know what you are doing!
1022
1023 LATEX_HEADER =
1024
1025 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
1026 # is prepared for conversion to pdf (using ps2pdf). The pdf file will
1027 # contain links (just like the HTML output) instead of page references
1028 # This makes the output suitable for online browsing using a pdf viewer.
1029
1030 PDF_HYPERLINKS = YES
1031
1032 # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
1033 # plain latex in the generated Makefile. Set this option to YES to get a
1034 # higher quality PDF documentation.
1035
1036 USE_PDFLATEX = @DOXYFILE_PDFLATEX@
1037
1038 # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
1039 # command to the generated LaTeX files. This will instruct LaTeX to keep
1040 # running if errors occur, instead of asking the user for help.
1041 # This option is also used when generating formulas in HTML.
1042
1043 LATEX_BATCHMODE = YES
1044
1045 # If LATEX_HIDE_INDICES is set to YES then doxygen will not
1046 # include the index chapters (such as File Index, Compound Index, etc.)
1047 # in the output.
1048
1049 LATEX_HIDE_INDICES = NO
1050
1051 #---------------------------------------------------------------------------
1052 # configuration options related to the RTF output
1053 #---------------------------------------------------------------------------
1054
1055 # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
1056 # The RTF output is optimized for Word 97 and may not look very pretty with
1057 # other RTF readers or editors.
1058
1059 GENERATE_RTF = NO
1060
1061 # The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
1062 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1063 # put in front of it. If left blank `rtf' will be used as the default path.
1064
1065 RTF_OUTPUT = rtf
1066
1067 # If the COMPACT_RTF tag is set to YES Doxygen generates more compact
1068 # RTF documents. This may be useful for small projects and may help to
1069 # save some trees in general.
1070
1071 COMPACT_RTF = NO
1072
1073 # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
1074 # will contain hyperlink fields. The RTF file will
1075 # contain links (just like the HTML output) instead of page references.
1076 # This makes the output suitable for online browsing using WORD or other
1077 # programs which support those fields.
1078 # Note: wordpad (write) and others do not support links.
1079
1080 RTF_HYPERLINKS = NO
1081
1082 # Load stylesheet definitions from file. Syntax is similar to doxygen's
1083 # config file, i.e. a series of assignments. You only have to provide
1084 # replacements, missing definitions are set to their default value.
1085
1086 RTF_STYLESHEET_FILE =
1087
1088 # Set optional variables used in the generation of an rtf document.
1089 # Syntax is similar to doxygen's config file.
1090
1091 RTF_EXTENSIONS_FILE =
1092
1093 #---------------------------------------------------------------------------
1094 # configuration options related to the man page output
1095 #---------------------------------------------------------------------------
1096
1097 # If the GENERATE_MAN tag is set to YES (the default) Doxygen will
1098 # generate man pages
1099
1100 GENERATE_MAN = NO
1101
1102 # The MAN_OUTPUT tag is used to specify where the man pages will be put.
1103 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1104 # put in front of it. If left blank `man' will be used as the default path.
1105
1106 MAN_OUTPUT = man
1107
1108 # The MAN_EXTENSION tag determines the extension that is added to
1109 # the generated man pages (default is the subroutine's section .3)
1110
1111 MAN_EXTENSION = .3
1112
1113 # If the MAN_LINKS tag is set to YES and Doxygen generates man output,
1114 # then it will generate one additional man file for each entity
1115 # documented in the real man page(s). These additional files
1116 # only source the real man page, but without them the man command
1117 # would be unable to find the correct page. The default is NO.
1118
1119 MAN_LINKS = NO
1120
1121 #---------------------------------------------------------------------------
1122 # configuration options related to the XML output
1123 #---------------------------------------------------------------------------
1124
1125 # If the GENERATE_XML tag is set to YES Doxygen will
1126 # generate an XML file that captures the structure of
1127 # the code including all documentation.
1128
1129 GENERATE_XML = NO
1130
1131 # The XML_OUTPUT tag is used to specify where the XML pages will be put.
1132 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1133 # put in front of it. If left blank `xml' will be used as the default path.
1134
1135 XML_OUTPUT = xml
1136
1137 # The XML_SCHEMA tag can be used to specify an XML schema,
1138 # which can be used by a validating XML parser to check the
1139 # syntax of the XML files.
1140
1141 XML_SCHEMA =
1142
1143 # The XML_DTD tag can be used to specify an XML DTD,
1144 # which can be used by a validating XML parser to check the
1145 # syntax of the XML files.
1146
1147 XML_DTD =
1148
1149 # If the XML_PROGRAMLISTING tag is set to YES Doxygen will
1150 # dump the program listings (including syntax highlighting
1151 # and cross-referencing information) to the XML output. Note that
1152 # enabling this will significantly increase the size of the XML output.
1153
1154 XML_PROGRAMLISTING = YES
1155
1156 #---------------------------------------------------------------------------
1157 # configuration options for the AutoGen Definitions output
1158 #---------------------------------------------------------------------------
1159
1160 # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
1161 # generate an AutoGen Definitions (see autogen.sf.net) file
1162 # that captures the structure of the code including all
1163 # documentation. Note that this feature is still experimental
1164 # and incomplete at the moment.
1165
1166 GENERATE_AUTOGEN_DEF = NO
1167
1168 #---------------------------------------------------------------------------
1169 # configuration options related to the Perl module output
1170 #---------------------------------------------------------------------------
1171
1172 # If the GENERATE_PERLMOD tag is set to YES Doxygen will
1173 # generate a Perl module file that captures the structure of
1174 # the code including all documentation. Note that this
1175 # feature is still experimental and incomplete at the
1176 # moment.
1177
1178 GENERATE_PERLMOD = NO
1179
1180 # If the PERLMOD_LATEX tag is set to YES Doxygen will generate
1181 # the necessary Makefile rules, Perl scripts and LaTeX code to be able
1182 # to generate PDF and DVI output from the Perl module output.
1183
1184 PERLMOD_LATEX = NO
1185
1186 # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
1187 # nicely formatted so it can be parsed by a human reader.
1188 # This is useful
1189 # if you want to understand what is going on.
1190 # On the other hand, if this
1191 # tag is set to NO the size of the Perl module output will be much smaller
1192 # and Perl will parse it just the same.
1193
1194 PERLMOD_PRETTY = YES
1195
1196 # The names of the make variables in the generated doxyrules.make file
1197 # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
1198 # This is useful so different doxyrules.make files included by the same
1199 # Makefile don't overwrite each other's variables.
1200
1201 PERLMOD_MAKEVAR_PREFIX =
1202
1203 #---------------------------------------------------------------------------
1204 # Configuration options related to the preprocessor
1205 #---------------------------------------------------------------------------
1206
1207 # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
1208 # evaluate all C-preprocessor directives found in the sources and include
1209 # files.
1210
1211 ENABLE_PREPROCESSING = YES
1212
1213 # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
1214 # names in the source code. If set to NO (the default) only conditional
1215 # compilation will be performed. Macro expansion can be done in a controlled
1216 # way by setting EXPAND_ONLY_PREDEF to YES.
1217
1218 MACRO_EXPANSION = NO
1219
1220 # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
1221 # then the macro expansion is limited to the macros specified with the
1222 # PREDEFINED and EXPAND_AS_DEFINED tags.
1223
1224 EXPAND_ONLY_PREDEF = NO
1225
1226 # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
1227 # in the INCLUDE_PATH (see below) will be search if a #include is found.
1228
1229 SEARCH_INCLUDES = YES
1230
1231 # The INCLUDE_PATH tag can be used to specify one or more directories that
1232 # contain include files that are not input files but should be processed by
1233 # the preprocessor.
1234
1235 INCLUDE_PATH =
1236
1237 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
1238 # patterns (like *.h and *.hpp) to filter out the header-files in the
1239 # directories. If left blank, the patterns specified with FILE_PATTERNS will
1240 # be used.
1241
1242 INCLUDE_FILE_PATTERNS =
1243
1244 # The PREDEFINED tag can be used to specify one or more macro names that
1245 # are defined before the preprocessor is started (similar to the -D option of
1246 # gcc). The argument of the tag is a list of macros of the form: name
1247 # or name=definition (no spaces). If the definition and the = are
1248 # omitted =1 is assumed. To prevent a macro definition from being
1249 # undefined via #undef or recursively expanded use the := operator
1250 # instead of the = operator.
1251
1252 PREDEFINED =
1253
1254 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
1255 # this tag can be used to specify a list of macro names that should be expanded.
1256 # The macro definition that is found in the sources will be used.
1257 # Use the PREDEFINED tag if you want to use a different macro definition.
1258
1259 EXPAND_AS_DEFINED =
1260
1261 # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
1262 # doxygen's preprocessor will remove all function-like macros that are alone
1263 # on a line, have an all uppercase name, and do not end with a semicolon. Such
1264 # function macros are typically used for boiler-plate code, and will confuse
1265 # the parser if not removed.
1266
1267 SKIP_FUNCTION_MACROS = YES
1268
1269 #---------------------------------------------------------------------------
1270 # Configuration::additions related to external references
1271 #---------------------------------------------------------------------------
1272
1273 # The TAGFILES option can be used to specify one or more tagfiles.
1274 # Optionally an initial location of the external documentation
1275 # can be added for each tagfile. The format of a tag file without
1276 # this location is as follows:
1277 #
1278 # TAGFILES = file1 file2 ...
1279 # Adding location for the tag files is done as follows:
1280 #
1281 # TAGFILES = file1=loc1 "file2 = loc2" ...
1282 # where "loc1" and "loc2" can be relative or absolute paths or
1283 # URLs. If a location is present for each tag, the installdox tool
1284 # does not have to be run to correct the links.
1285 # Note that each tag file must have a unique name
1286 # (where the name does NOT include the path)
1287 # If a tag file is not located in the directory in which doxygen
1288 # is run, you must also specify the path to the tagfile here.
1289
1290 TAGFILES =
1291
1292 # When a file name is specified after GENERATE_TAGFILE, doxygen will create
1293 # a tag file that is based on the input files it reads.
1294
1295 GENERATE_TAGFILE =
1296
1297 # If the ALLEXTERNALS tag is set to YES all external classes will be listed
1298 # in the class index. If set to NO only the inherited external classes
1299 # will be listed.
1300
1301 ALLEXTERNALS = NO
1302
1303 # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
1304 # in the modules index. If set to NO, only the current project's groups will
1305 # be listed.
1306
1307 EXTERNAL_GROUPS = YES
1308
1309 # The PERL_PATH should be the absolute path and name of the perl script
1310 # interpreter (i.e. the result of `which perl').
1311
1312 PERL_PATH = /usr/bin/perl
1313
1314 #---------------------------------------------------------------------------
1315 # Configuration options related to the dot tool
1316 #---------------------------------------------------------------------------
1317
1318 # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
1319 # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
1320 # or super classes. Setting the tag to NO turns the diagrams off. Note that
1321 # this option is superseded by the HAVE_DOT option below. This is only a
1322 # fallback. It is recommended to install and use dot, since it yields more
1323 # powerful graphs.
1324
1325 CLASS_DIAGRAMS = YES
1326
1327 # You can define message sequence charts within doxygen comments using the \msc
1328 # command. Doxygen will then run the mscgen tool (see
1329 # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
1330 # documentation. The MSCGEN_PATH tag allows you to specify the directory where
1331 # the mscgen tool resides. If left empty the tool is assumed to be found in the
1332 # default search path.
1333
1334 MSCGEN_PATH =
1335
1336 # If set to YES, the inheritance and collaboration graphs will hide
1337 # inheritance and usage relations if the target is undocumented
1338 # or is not a class.
1339
1340 HIDE_UNDOC_RELATIONS = YES
1341
1342 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
1343 # available from the path. This tool is part of Graphviz, a graph visualization
1344 # toolkit from AT&T and Lucent Bell Labs. The other options in this section
1345 # have no effect if this option is set to NO (the default)
1346
1347 HAVE_DOT = @DOXYFILE_DOT@
1348
1349 # By default doxygen will write a font called FreeSans.ttf to the output
1350 # directory and reference it in all dot files that doxygen generates. This
1351 # font does not include all possible unicode characters however, so when you need
1352 # these (or just want a differently looking font) you can specify the font name
1353 # using DOT_FONTNAME. You need need to make sure dot is able to find the font,
1354 # which can be done by putting it in a standard location or by setting the
1355 # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
1356 # containing the font.
1357
1358 DOT_FONTNAME = FreeSans
1359
1360 # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
1361 # The default size is 10pt.
1362
1363 DOT_FONTSIZE = 10
1364
1365 # By default doxygen will tell dot to use the output directory to look for the
1366 # FreeSans.ttf font (which doxygen will put there itself). If you specify a
1367 # different font using DOT_FONTNAME you can set the path where dot
1368 # can find it using this tag.
1369
1370 DOT_FONTPATH =
1371
1372 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
1373 # will generate a graph for each documented class showing the direct and
1374 # indirect inheritance relations. Setting this tag to YES will force the
1375 # the CLASS_DIAGRAMS tag to NO.
1376
1377 CLASS_GRAPH = YES
1378
1379 # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
1380 # will generate a graph for each documented class showing the direct and
1381 # indirect implementation dependencies (inheritance, containment, and
1382 # class references variables) of the class with other documented classes.
1383
1384 COLLABORATION_GRAPH = YES
1385
1386 # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
1387 # will generate a graph for groups, showing the direct groups dependencies
1388
1389 GROUP_GRAPHS = YES
1390
1391 # If the UML_LOOK tag is set to YES doxygen will generate inheritance and
1392 # collaboration diagrams in a style similar to the OMG's Unified Modeling
1393 # Language.
1394
1395 UML_LOOK = NO
1396
1397 # If set to YES, the inheritance and collaboration graphs will show the
1398 # relations between templates and their instances.
1399
1400 TEMPLATE_RELATIONS = NO
1401
1402 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
1403 # tags are set to YES then doxygen will generate a graph for each documented
1404 # file showing the direct and indirect include dependencies of the file with
1405 # other documented files.
1406
1407 INCLUDE_GRAPH = NO
1408
1409 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
1410 # HAVE_DOT tags are set to YES then doxygen will generate a graph for each
1411 # documented header file showing the documented files that directly or
1412 # indirectly include this file.
1413
1414 INCLUDED_BY_GRAPH = YES
1415
1416 # If the CALL_GRAPH and HAVE_DOT options are set to YES then
1417 # doxygen will generate a call dependency graph for every global function
1418 # or class method. Note that enabling this option will significantly increase
1419 # the time of a run. So in most cases it will be better to enable call graphs
1420 # for selected functions only using the \callgraph command.
1421
1422 CALL_GRAPH = NO
1423
1424 # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
1425 # doxygen will generate a caller dependency graph for every global function
1426 # or class method. Note that enabling this option will significantly increase
1427 # the time of a run. So in most cases it will be better to enable caller
1428 # graphs for selected functions only using the \callergraph command.
1429
1430 CALLER_GRAPH = NO
1431
1432 # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
1433 # will graphical hierarchy of all classes instead of a textual one.
1434
1435 GRAPHICAL_HIERARCHY = YES
1436
1437 # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
1438 # then doxygen will show the dependencies a directory has on other directories
1439 # in a graphical way. The dependency relations are determined by the #include
1440 # relations between the files in the directories.
1441
1442 DIRECTORY_GRAPH = YES
1443
1444 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
1445 # generated by dot. Possible values are png, jpg, or gif
1446 # If left blank png will be used.
1447
1448 DOT_IMAGE_FORMAT = png
1449
1450 # The tag DOT_PATH can be used to specify the path where the dot tool can be
1451 # found. If left blank, it is assumed the dot tool can be found in the path.
1452
1453 DOT_PATH = "@DOXYGEN_DOT_PATH@"
1454
1455 # The DOTFILE_DIRS tag can be used to specify one or more directories that
1456 # contain dot files that are included in the documentation (see the
1457 # \dotfile command).
1458
1459 DOTFILE_DIRS =
1460
1461 # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
1462 # nodes that will be shown in the graph. If the number of nodes in a graph
1463 # becomes larger than this value, doxygen will truncate the graph, which is
1464 # visualized by representing a node as a red box. Note that doxygen if the
1465 # number of direct children of the root node in a graph is already larger than
1466 # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
1467 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
1468
1469 DOT_GRAPH_MAX_NODES = 50
1470
1471 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
1472 # graphs generated by dot. A depth value of 3 means that only nodes reachable
1473 # from the root by following a path via at most 3 edges will be shown. Nodes
1474 # that lay further from the root node will be omitted. Note that setting this
1475 # option to 1 or 2 may greatly reduce the computation time needed for large
1476 # code bases. Also note that the size of a graph can be further restricted by
1477 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
1478
1479 MAX_DOT_GRAPH_DEPTH = 0
1480
1481 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
1482 # background. This is disabled by default, because dot on Windows does not
1483 # seem to support this out of the box. Warning: Depending on the platform used,
1484 # enabling this option may lead to badly anti-aliased labels on the edges of
1485 # a graph (i.e. they become hard to read).
1486
1487 DOT_TRANSPARENT = YES
1488
1489 # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
1490 # files in one run (i.e. multiple -o and -T options on the command line). This
1491 # makes dot run faster, but since only newer versions of dot (>1.8.10)
1492 # support this, this feature is disabled by default.
1493
1494 DOT_MULTI_TARGETS = NO
1495
1496 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
1497 # generate a legend page explaining the meaning of the various boxes and
1498 # arrows in the dot generated graphs.
1499
1500 GENERATE_LEGEND = YES
1501
1502 # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
1503 # remove the intermediate dot files that are used to generate
1504 # the various graphs.
1505
1506 DOT_CLEANUP = YES
1507
1508 #---------------------------------------------------------------------------
1509 # Options related to the search engine
1510 #---------------------------------------------------------------------------
1511
1512 # The SEARCHENGINE tag specifies whether or not a search engine should be
1513 # used. If set to NO the values of all tags below this one will be ignored.
1514
1515 SEARCHENGINE = NO
0 Copyright (c) 2014, Washington University School of Medicine
1 All rights reserved.
2
3 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
5 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
7 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
9 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 The CiftiLib library requires boost headers and either QT (4.8.x or 5), or libxml++ 2.17.x or newer (and its dependencies: libxml2, glib, sigc++, gtkmm and glibmm) and the boost filesystem library to compile, and optionally uses zlib if you want to use its NIfTI reading capabilities for other purposes.
1
2 It is currently set up to be compiled, along with a few simple examples, by cmake:
3
4 #start one level up from the source tree
5 #make build directory beside the source tree, enter it
6 mkdir build; cd build
7
8 #you may want to set the cmake variable CMAKE_BUILD_TYPE to Release or Debug before building
9 #the default build behavior may be non-optimized and without debug symbols.
10
11 #run cmake to generate makefiles
12 cmake ../CiftiLib -D CMAKE_BUILD_TYPE=Release
13 #OR
14 cmake-gui ../CiftiLib
15
16 #build
17 make
18
19 #OPTIONAL: run tests, make docs
20 make test
21 make doc
22
23 The resulting library and example executables will be in subdirectories of the build directory, not in the source directory. You can install the library and headers (location controlled by cmake variable CMAKE_INSTALL_PREFIX and make variable DESTDIR) with 'make install'.
24
25 Troubleshooting:
26
27 If you are using manually-installed libraries rather than distribution-packaged libraries, you may need to use the cmake variables CMAKE_LIBRARY_PATH and CMAKE_INCLUDE_PATH.
28
29 If you are getting link errors related to boost, try setting a cmake variable Boost_NO_BOOST_CMAKE to true.
0 The main object for dealing with Cifti is CiftiFile. To set up a new CiftiFile,
1 make a CiftiXML object with the mappings you want, and call setCiftiXML on the CiftiFile object.
2
3 The XML tells you things about the mappings, and lets you set new mappings or modify existing ones:
4
5 int64_t rowLength = myXML.getDimensionLength(CiftiXML::ALONG_ROW);
6 CiftiMappingType::MappingType rowType = myXML.getMappingType(CiftiXML::ALONG_ROW);
7 if (rowType == CiftiMappingType::SCALARS)
8 {
9 CiftiScalarsMap& myScalarMap = myXML.getScalarsMap(CiftiXML::ALONG_ROW);
10 QString firstMapName = myScalarMap.getMapName(0);
11 ...
12 myScalarMap.setLength(1);
13 }
14
15 See the rewrite example for how to read and write data to CiftiFile.
16
17 CiftiFile internally uses NiftiIO, which is a NIfTI reader for both NIfTI-1 and NIfTI-2 single-file (.nii),
18 including reading .nii.gz files if zlib is found (NOTE: .nii.gz should not be used for CIFTI files,
19 as seeking is slow, and seeking backwards is impossible).
20
21 Our nifti1.h and nifti2.h are slightly modified, replacing the #defines of standard values for header fields
22 with constant integers. We also declare CIFTI-specific intent codes and the extension code in nifti2.h,
23 and have some macros for determining header version.
24
0
1 #use pkg-config
2 FIND_PACKAGE(PkgConfig)
3 PKG_CHECK_MODULES(PC_GLIB glib-2.0)
4
5 FIND_PATH(glib_INCLUDE_DIR NAMES glib.h HINTS ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS})
6 FIND_PATH(glib_config_INCLUDE_DIR NAMES glibconfig.h HINTS ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS})
7 FIND_LIBRARY(glib_LIBRARY NAMES glib glib-2.0 HINTS ${PC_GLIB_LIBDIR} ${PC_GLIB_LIBRARY_DIRS})
8
9 SET(glib_LIBRARIES ${glib_LIBRARY} ${PC_GLIB_PKGCONF_LIBRARIES})
10 SET(glib_INCLUDE_DIRS ${glib_INCLUDE_DIR} ${glib_config_INCLUDE_DIR} ${PC_GLIB_PKGCONF_INCLUDE_DIRS})
11
12 INCLUDE(FindPackageHandleStandardArgs)
13 FIND_PACKAGE_HANDLE_STANDARD_ARGS(glib DEFAULT_MSG glib_LIBRARY glib_INCLUDE_DIR)
14
15 MARK_AS_ADVANCED(glib_INCLUDE_DIR glib_config_INCLUDE_DIR glib_LIBRARY)
0
1 IF(glibmm_FIND_REQUIRED)
2 FIND_PACKAGE(glib REQUIRED)
3 FIND_PACKAGE(sigc++ REQUIRED)
4 ELSE(glibmm_FIND_REQUIRED)
5 FIND_PACKAGE(glib)
6 FIND_PACKAGE(sigc++)
7 ENDIF(glibmm_FIND_REQUIRED)
8
9 IF(GLIB_FOUND)
10
11 #use pkg-config
12 FIND_PACKAGE(PkgConfig)
13 PKG_CHECK_MODULES(PC_GLIBMM glibmm-2.4)
14
15 FIND_PATH(glibmm_INCLUDE_DIR NAMES glibmm/main.h HINTS ${PC_GLIBMM_INCLUDEDIR} ${PC_GLIBMM_INCLUDE_DIRS})
16 FIND_PATH(glibmm_config_INCLUDE_DIR NAMES glibmmconfig.h HINTS ${PC_GLIBMM_INCLUDEDIR} ${PC_GLIBMM_INCLUDE_DIRS})
17 FIND_LIBRARY(glibmm_LIBRARY NAMES glibmm glibmm-2.4 HINTS ${PC_GLIBMM_LIBDIR} ${PC_GLIBMM_LIBRARY_DIRS})
18
19 SET(glibmm_LIBRARIES ${glibmm_LIBRARY} ${PC_GLIBMM_PKGCONF_LIBRARIES} ${glib_LIBRARIES} ${sigc++_LIBRARIES})
20 SET(glibmm_INCLUDE_DIRS ${glibmm_INCLUDE_DIR} ${glibmm_config_INCLUDE_DIR} ${PC_GLIBMM_PKGCONF_INCLUDE_DIRS} ${glib_INCLUDE_DIRS} ${sigc++_INCLUDE_DIRS})
21
22 ENDIF(GLIB_FOUND)
23
24 INCLUDE(FindPackageHandleStandardArgs)
25 FIND_PACKAGE_HANDLE_STANDARD_ARGS(glibmm DEFAULT_MSG glibmm_LIBRARY glibmm_INCLUDE_DIR)
26
27 MARK_AS_ADVANCED(glibmm_INCLUDE_DIR glibmm_config_INCLUDE_DIR glibmm_LIBRARY)
0
1 IF(libxml++_FIND_REQUIRED)
2 FIND_PACKAGE(glibmm REQUIRED)
3 FIND_PACKAGE(LibXml2 REQUIRED)
4 ELSE(libxml++_FIND_REQUIRED)
5 FIND_PACKAGE(glibmm)
6 FIND_PACKAGE(LibXml2)
7 ENDIF(libxml++_FIND_REQUIRED)
8
9 IF(GLIBMM_FOUND AND LIBXML2_FOUND)
10
11 #use pkg-config
12 FIND_PACKAGE(PkgConfig)
13 PKG_CHECK_MODULES(PC_LIBXMLPP QUIET libxml++-2.6)
14
15 FIND_PATH(libxml++_INCLUDE_DIR NAMES libxml++/libxml++.h HINTS ${PC_LIBXMLPP_INCLUDEDIR} ${PC_LIBXMLPP_INCLUDE_DIRS})
16 FIND_PATH(libxml++_config_INCLUDE_DIR NAMES libxml++config.h HINTS ${PC_LIBXMLPP_INCLUDEDIR} ${PC_LIBXMLPP_INCLUDE_DIRS})
17 FIND_LIBRARY(libxml++_LIBRARY NAMES xml++ xml++-2.6 HINTS ${PC_LIBXMLPP_LIBDIR} ${PC_LIBXMLPP_LIBRARY_DIRS})
18
19 SET(libxml++_LIBRARIES ${libxml++_LIBRARY} ${PC_LIBXMLPP_PKGCONF_LIBRARIES} ${glibmm_LIBRARIES} ${LIBXML2_LIBRARIES})
20 IF(libxml++_config_INCLUDE_DIR)
21 SET(libxml++_INCLUDE_DIRS ${libxml++_INCLUDE_DIR} ${PC_LIBXMLPP_PKGCONF_INCLUDE_DIRS} ${libxml++_config_INCLUDE_DIR} ${glibmm_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR})
22 ELSE(libxml++_config_INCLUDE_DIR)
23 SET(libxml++_INCLUDE_DIRS ${libxml++_INCLUDE_DIR} ${PC_LIBXMLPP_PKGCONF_INCLUDE_DIRS} ${glibmm_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR})
24 ENDIF(libxml++_config_INCLUDE_DIR)
25
26 ENDIF(GLIBMM_FOUND AND LIBXML2_FOUND)
27
28 INCLUDE(FindPackageHandleStandardArgs)
29 FIND_PACKAGE_HANDLE_STANDARD_ARGS(libxml++ DEFAULT_MSG libxml++_LIBRARY libxml++_INCLUDE_DIR)
30
31 MARK_AS_ADVANCED(libxml++_INCLUDE_DIR libxml++_config_INCLUDE_DIR libxml++_LIBRARY)
0
1 #use pkg-config
2 FIND_PACKAGE(PkgConfig)
3 PKG_CHECK_MODULES(PC_SIGCXX sigc++-2.0)
4
5 FIND_PATH(sigc++_INCLUDE_DIR NAMES sigc++/sigc++.h HINTS ${PC_SIGCXX_INCLUDEDIR} ${PC_SIGCXX_INCLUDE_DIRS})
6 FIND_PATH(sigc++_config_INCLUDE_DIR NAMES sigc++config.h HINTS ${PC_SIGCXX_INCLUDEDIR} ${PC_SIGCXX_INCLUDE_DIRS})
7 FIND_LIBRARY(sigc++_LIBRARY NAMES sigc sigc-2.0 HINTS ${PC_SIGCXX_LIBDIR} ${PC_SIGCXX_LIBRARY_DIRS})
8
9 SET(sigc++_LIBRARIES ${sigc++_LIBRARY} ${PC_SIGCXX_PKGCONF_LIBRARIES})
10 SET(sigc++_INCLUDE_DIRS ${sigc++_INCLUDE_DIR} ${sigc++_config_INCLUDE_DIR} ${PC_SIGCXX_PKGCONF_INCLUDE_DIRS})
11
12 INCLUDE(FindPackageHandleStandardArgs)
13 FIND_PACKAGE_HANDLE_STANDARD_ARGS(sigc++ DEFAULT_MSG sigc++_LIBRARY sigc++_INCLUDE_DIR)
14
15 MARK_AS_ADVANCED(sigc++_INCLUDE_DIR sigc++_config_INCLUDE_DIR sigc++_LIBRARY)
0 cmake_minimum_required(VERSION 2.6)
1
2 project(UseDoxygen)
3
4 set(CMAKE_INSTALL_PREFIX ${CMAKE_ROOT})
5
6 install(FILES UseDoxygen.cmake Doxyfile.in
7 DESTINATION "Modules")
8
9 configure_file(
10 "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
11 "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
12 IMMEDIATE @ONLY)
13
14 add_custom_target(uninstall
15 COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
0 Redistribution and use in source and binary forms, with or without
1 modification, are permitted provided that the following conditions
2 are met:
3
4 1. Redistributions of source code must retain the copyright
5 notice, this list of conditions and the following disclaimer.
6 2. Redistributions in binary form must reproduce the copyright
7 notice, this list of conditions and the following disclaimer in the
8 documentation and/or other materials provided with the distribution.
9 3. The name of the author may not be used to endorse or promote products
10 derived from this software without specific prior written permission.
11
12 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
13 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
14 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
15 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
16 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
17 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
18 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
19 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 # Doxyfile 1.5.8
1
2 # This file describes the settings to be used by the documentation system
3 # doxygen (www.doxygen.org) for a project
4 #
5 # All text after a hash (#) is considered a comment and will be ignored
6 # The format is:
7 # TAG = value [value, ...]
8 # For lists items can also be appended using:
9 # TAG += value [value, ...]
10 # Values that contain spaces should be placed between quotes (" ")
11
12 #---------------------------------------------------------------------------
13 # Project related configuration options
14 #---------------------------------------------------------------------------
15
16 # This tag specifies the encoding used for all characters in the config file
17 # that follow. The default is UTF-8 which is also the encoding used for all
18 # text before the first occurrence of this tag. Doxygen uses libiconv (or the
19 # iconv built into libc) for the transcoding. See
20 # http://www.gnu.org/software/libiconv for the list of possible encodings.
21
22 DOXYFILE_ENCODING = UTF-8
23
24 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded
25 # by quotes) that should identify the project.
26
27 PROJECT_NAME = "@PROJECT_NAME@"
28
29 # The PROJECT_NUMBER tag can be used to enter a project or revision number.
30 # This could be handy for archiving the generated documentation or
31 # if some version control system is used.
32
33 PROJECT_NUMBER = "@PROJECT_VERSION@"
34
35 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
36 # base path where the generated documentation will be put.
37 # If a relative path is entered, it will be relative to the location
38 # where doxygen was started. If left blank the current directory will be used.
39
40 OUTPUT_DIRECTORY = "@DOXYFILE_OUTPUT_DIR@"
41
42 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
43 # 4096 sub-directories (in 2 levels) under the output directory of each output
44 # format and will distribute the generated files over these directories.
45 # Enabling this option can be useful when feeding doxygen a huge amount of
46 # source files, where putting all generated files in the same directory would
47 # otherwise cause performance problems for the file system.
48
49 CREATE_SUBDIRS = NO
50
51 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
52 # documentation generated by doxygen is written. Doxygen will use this
53 # information to generate all constant output in the proper language.
54 # The default language is English, other supported languages are:
55 # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
56 # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
57 # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
58 # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
59 # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
60 # Spanish, Swedish, and Ukrainian.
61
62 OUTPUT_LANGUAGE = English
63
64 # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
65 # include brief member descriptions after the members that are listed in
66 # the file and class documentation (similar to JavaDoc).
67 # Set to NO to disable this.
68
69 BRIEF_MEMBER_DESC = YES
70
71 # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
72 # the brief description of a member or function before the detailed description.
73 # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
74 # brief descriptions will be completely suppressed.
75
76 REPEAT_BRIEF = YES
77
78 # This tag implements a quasi-intelligent brief description abbreviator
79 # that is used to form the text in various listings. Each string
80 # in this list, if found as the leading text of the brief description, will be
81 # stripped from the text and the result after processing the whole list, is
82 # used as the annotated text. Otherwise, the brief description is used as-is.
83 # If left blank, the following values are used ("$name" is automatically
84 # replaced with the name of the entity): "The $name class" "The $name widget"
85 # "The $name file" "is" "provides" "specifies" "contains"
86 # "represents" "a" "an" "the"
87
88 ABBREVIATE_BRIEF =
89
90 # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
91 # Doxygen will generate a detailed section even if there is only a brief
92 # description.
93
94 ALWAYS_DETAILED_SEC = NO
95
96 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
97 # inherited members of a class in the documentation of that class as if those
98 # members were ordinary class members. Constructors, destructors and assignment
99 # operators of the base classes will not be shown.
100
101 INLINE_INHERITED_MEMB = NO
102
103 # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
104 # path before files name in the file list and in the header files. If set
105 # to NO the shortest path that makes the file name unique will be used.
106
107 FULL_PATH_NAMES = NO
108
109 # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
110 # can be used to strip a user-defined part of the path. Stripping is
111 # only done if one of the specified strings matches the left-hand part of
112 # the path. The tag can be used to show relative paths in the file list.
113 # If left blank the directory from which doxygen is run is used as the
114 # path to strip.
115
116 STRIP_FROM_PATH =
117
118 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
119 # the path mentioned in the documentation of a class, which tells
120 # the reader which header file to include in order to use a class.
121 # If left blank only the name of the header file containing the class
122 # definition is used. Otherwise one should specify the include paths that
123 # are normally passed to the compiler using the -I flag.
124
125 STRIP_FROM_INC_PATH =
126
127 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
128 # (but less readable) file names. This can be useful is your file systems
129 # doesn't support long names like on DOS, Mac, or CD-ROM.
130
131 SHORT_NAMES = NO
132
133 # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
134 # will interpret the first line (until the first dot) of a JavaDoc-style
135 # comment as the brief description. If set to NO, the JavaDoc
136 # comments will behave just like regular Qt-style comments
137 # (thus requiring an explicit @brief command for a brief description.)
138
139 JAVADOC_AUTOBRIEF = NO
140
141 # If the QT_AUTOBRIEF tag is set to YES then Doxygen will
142 # interpret the first line (until the first dot) of a Qt-style
143 # comment as the brief description. If set to NO, the comments
144 # will behave just like regular Qt-style comments (thus requiring
145 # an explicit \brief command for a brief description.)
146
147 QT_AUTOBRIEF = NO
148
149 # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
150 # treat a multi-line C++ special comment block (i.e. a block of //! or ///
151 # comments) as a brief description. This used to be the default behaviour.
152 # The new default is to treat a multi-line C++ comment block as a detailed
153 # description. Set this tag to YES if you prefer the old behaviour instead.
154
155 MULTILINE_CPP_IS_BRIEF = NO
156
157 # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
158 # member inherits the documentation from any documented member that it
159 # re-implements.
160
161 INHERIT_DOCS = YES
162
163 # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
164 # a new page for each member. If set to NO, the documentation of a member will
165 # be part of the file/class/namespace that contains it.
166
167 SEPARATE_MEMBER_PAGES = NO
168
169 # The TAB_SIZE tag can be used to set the number of spaces in a tab.
170 # Doxygen uses this value to replace tabs by spaces in code fragments.
171
172 TAB_SIZE = 8
173
174 # This tag can be used to specify a number of aliases that acts
175 # as commands in the documentation. An alias has the form "name=value".
176 # For example adding "sideeffect=\par Side Effects:\n" will allow you to
177 # put the command \sideeffect (or @sideeffect) in the documentation, which
178 # will result in a user-defined paragraph with heading "Side Effects:".
179 # You can put \n's in the value part of an alias to insert newlines.
180
181 ALIASES =
182
183 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
184 # sources only. Doxygen will then generate output that is more tailored for C.
185 # For instance, some of the names that are used will be different. The list
186 # of all members will be omitted, etc.
187
188 OPTIMIZE_OUTPUT_FOR_C = NO
189
190 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
191 # sources only. Doxygen will then generate output that is more tailored for
192 # Java. For instance, namespaces will be presented as packages, qualified
193 # scopes will look different, etc.
194
195 OPTIMIZE_OUTPUT_JAVA = NO
196
197 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
198 # sources only. Doxygen will then generate output that is more tailored for
199 # Fortran.
200
201 OPTIMIZE_FOR_FORTRAN = NO
202
203 # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
204 # sources. Doxygen will then generate output that is tailored for
205 # VHDL.
206
207 OPTIMIZE_OUTPUT_VHDL = NO
208
209 # Doxygen selects the parser to use depending on the extension of the files it parses.
210 # With this tag you can assign which parser to use for a given extension.
211 # Doxygen has a built-in mapping, but you can override or extend it using this tag.
212 # The format is ext=language, where ext is a file extension, and language is one of
213 # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
214 # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
215 # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
216 # use: inc=Fortran f=C
217
218 EXTENSION_MAPPING =
219
220 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
221 # to include (a tag file for) the STL sources as input, then you should
222 # set this tag to YES in order to let doxygen match functions declarations and
223 # definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
224 # func(std::string) {}). This also make the inheritance and collaboration
225 # diagrams that involve STL classes more complete and accurate.
226
227 BUILTIN_STL_SUPPORT = NO
228
229 # If you use Microsoft's C++/CLI language, you should set this option to YES to
230 # enable parsing support.
231
232 CPP_CLI_SUPPORT = NO
233
234 # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
235 # Doxygen will parse them like normal C++ but will assume all classes use public
236 # instead of private inheritance when no explicit protection keyword is present.
237
238 SIP_SUPPORT = NO
239
240 # For Microsoft's IDL there are propget and propput attributes to indicate getter
241 # and setter methods for a property. Setting this option to YES (the default)
242 # will make doxygen to replace the get and set methods by a property in the
243 # documentation. This will only work if the methods are indeed getting or
244 # setting a simple type. If this is not the case, or you want to show the
245 # methods anyway, you should set this option to NO.
246
247 IDL_PROPERTY_SUPPORT = YES
248
249 # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
250 # tag is set to YES, then doxygen will reuse the documentation of the first
251 # member in the group (if any) for the other members of the group. By default
252 # all members of a group must be documented explicitly.
253
254 DISTRIBUTE_GROUP_DOC = NO
255
256 # Set the SUBGROUPING tag to YES (the default) to allow class member groups of
257 # the same type (for instance a group of public functions) to be put as a
258 # subgroup of that type (e.g. under the Public Functions section). Set it to
259 # NO to prevent subgrouping. Alternatively, this can be done per class using
260 # the \nosubgrouping command.
261
262 SUBGROUPING = YES
263
264 # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
265 # is documented as struct, union, or enum with the name of the typedef. So
266 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
267 # with name TypeT. When disabled the typedef will appear as a member of a file,
268 # namespace, or class. And the struct will be named TypeS. This can typically
269 # be useful for C code in case the coding convention dictates that all compound
270 # types are typedef'ed and only the typedef is referenced, never the tag name.
271
272 TYPEDEF_HIDES_STRUCT = NO
273
274 # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
275 # determine which symbols to keep in memory and which to flush to disk.
276 # When the cache is full, less often used symbols will be written to disk.
277 # For small to medium size projects (<1000 input files) the default value is
278 # probably good enough. For larger projects a too small cache size can cause
279 # doxygen to be busy swapping symbols to and from disk most of the time
280 # causing a significant performance penality.
281 # If the system has enough physical memory increasing the cache will improve the
282 # performance by keeping more symbols in memory. Note that the value works on
283 # a logarithmic scale so increasing the size by one will rougly double the
284 # memory usage. The cache size is given by this formula:
285 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
286 # corresponding to a cache size of 2^16 = 65536 symbols
287
288 SYMBOL_CACHE_SIZE = 0
289
290 #---------------------------------------------------------------------------
291 # Build related configuration options
292 #---------------------------------------------------------------------------
293
294 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
295 # documentation are documented, even if no documentation was available.
296 # Private class members and static file members will be hidden unless
297 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
298
299 EXTRACT_ALL = NO
300
301 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class
302 # will be included in the documentation.
303
304 EXTRACT_PRIVATE = NO
305
306 # If the EXTRACT_STATIC tag is set to YES all static members of a file
307 # will be included in the documentation.
308
309 EXTRACT_STATIC = NO
310
311 # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
312 # defined locally in source files will be included in the documentation.
313 # If set to NO only classes defined in header files are included.
314
315 EXTRACT_LOCAL_CLASSES = YES
316
317 # This flag is only useful for Objective-C code. When set to YES local
318 # methods, which are defined in the implementation section but not in
319 # the interface are included in the documentation.
320 # If set to NO (the default) only methods in the interface are included.
321
322 EXTRACT_LOCAL_METHODS = NO
323
324 # If this flag is set to YES, the members of anonymous namespaces will be
325 # extracted and appear in the documentation as a namespace called
326 # 'anonymous_namespace{file}', where file will be replaced with the base
327 # name of the file that contains the anonymous namespace. By default
328 # anonymous namespace are hidden.
329
330 EXTRACT_ANON_NSPACES = NO
331
332 # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
333 # undocumented members of documented classes, files or namespaces.
334 # If set to NO (the default) these members will be included in the
335 # various overviews, but no documentation section is generated.
336 # This option has no effect if EXTRACT_ALL is enabled.
337
338 HIDE_UNDOC_MEMBERS = NO
339
340 # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
341 # undocumented classes that are normally visible in the class hierarchy.
342 # If set to NO (the default) these classes will be included in the various
343 # overviews. This option has no effect if EXTRACT_ALL is enabled.
344
345 HIDE_UNDOC_CLASSES = NO
346
347 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
348 # friend (class|struct|union) declarations.
349 # If set to NO (the default) these declarations will be included in the
350 # documentation.
351
352 HIDE_FRIEND_COMPOUNDS = NO
353
354 # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
355 # documentation blocks found inside the body of a function.
356 # If set to NO (the default) these blocks will be appended to the
357 # function's detailed documentation block.
358
359 HIDE_IN_BODY_DOCS = NO
360
361 # The INTERNAL_DOCS tag determines if documentation
362 # that is typed after a \internal command is included. If the tag is set
363 # to NO (the default) then the documentation will be excluded.
364 # Set it to YES to include the internal documentation.
365
366 INTERNAL_DOCS = NO
367
368 # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
369 # file names in lower-case letters. If set to YES upper-case letters are also
370 # allowed. This is useful if you have classes or files whose names only differ
371 # in case and if your file system supports case sensitive file names. Windows
372 # and Mac users are advised to set this option to NO.
373
374 CASE_SENSE_NAMES = YES
375
376 # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
377 # will show members with their full class and namespace scopes in the
378 # documentation. If set to YES the scope will be hidden.
379
380 HIDE_SCOPE_NAMES = NO
381
382 # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
383 # will put a list of the files that are included by a file in the documentation
384 # of that file.
385
386 SHOW_INCLUDE_FILES = YES
387
388 # If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
389 # is inserted in the documentation for inline members.
390
391 INLINE_INFO = YES
392
393 # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
394 # will sort the (detailed) documentation of file and class members
395 # alphabetically by member name. If set to NO the members will appear in
396 # declaration order.
397
398 SORT_MEMBER_DOCS = YES
399
400 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
401 # brief documentation of file, namespace and class members alphabetically
402 # by member name. If set to NO (the default) the members will appear in
403 # declaration order.
404
405 SORT_BRIEF_DOCS = NO
406
407 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
408 # hierarchy of group names into alphabetical order. If set to NO (the default)
409 # the group names will appear in their defined order.
410
411 SORT_GROUP_NAMES = NO
412
413 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
414 # sorted by fully-qualified names, including namespaces. If set to
415 # NO (the default), the class list will be sorted only by class name,
416 # not including the namespace part.
417 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
418 # Note: This option applies only to the class list, not to the
419 # alphabetical list.
420
421 SORT_BY_SCOPE_NAME = NO
422
423 # The GENERATE_TODOLIST tag can be used to enable (YES) or
424 # disable (NO) the todo list. This list is created by putting \todo
425 # commands in the documentation.
426
427 GENERATE_TODOLIST = YES
428
429 # The GENERATE_TESTLIST tag can be used to enable (YES) or
430 # disable (NO) the test list. This list is created by putting \test
431 # commands in the documentation.
432
433 GENERATE_TESTLIST = YES
434
435 # The GENERATE_BUGLIST tag can be used to enable (YES) or
436 # disable (NO) the bug list. This list is created by putting \bug
437 # commands in the documentation.
438
439 GENERATE_BUGLIST = YES
440
441 # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
442 # disable (NO) the deprecated list. This list is created by putting
443 # \deprecated commands in the documentation.
444
445 GENERATE_DEPRECATEDLIST= YES
446
447 # The ENABLED_SECTIONS tag can be used to enable conditional
448 # documentation sections, marked by \if sectionname ... \endif.
449
450 ENABLED_SECTIONS =
451
452 # The MAX_INITIALIZER_LINES tag determines the maximum number of lines
453 # the initial value of a variable or define consists of for it to appear in
454 # the documentation. If the initializer consists of more lines than specified
455 # here it will be hidden. Use a value of 0 to hide initializers completely.
456 # The appearance of the initializer of individual variables and defines in the
457 # documentation can be controlled using \showinitializer or \hideinitializer
458 # command in the documentation regardless of this setting.
459
460 MAX_INITIALIZER_LINES = 30
461
462 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated
463 # at the bottom of the documentation of classes and structs. If set to YES the
464 # list will mention the files that were used to generate the documentation.
465
466 SHOW_USED_FILES = YES
467
468 # If the sources in your project are distributed over multiple directories
469 # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
470 # in the documentation. The default is NO.
471
472 SHOW_DIRECTORIES = NO
473
474 # Set the SHOW_FILES tag to NO to disable the generation of the Files page.
475 # This will remove the Files entry from the Quick Index and from the
476 # Folder Tree View (if specified). The default is YES.
477
478 SHOW_FILES = YES
479
480 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the
481 # Namespaces page.
482 # This will remove the Namespaces entry from the Quick Index
483 # and from the Folder Tree View (if specified). The default is YES.
484
485 SHOW_NAMESPACES = YES
486
487 # The FILE_VERSION_FILTER tag can be used to specify a program or script that
488 # doxygen should invoke to get the current version for each file (typically from
489 # the version control system). Doxygen will invoke the program by executing (via
490 # popen()) the command <command> <input-file>, where <command> is the value of
491 # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
492 # provided by doxygen. Whatever the program writes to standard output
493 # is used as the file version. See the manual for examples.
494
495 FILE_VERSION_FILTER =
496
497 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
498 # doxygen. The layout file controls the global structure of the generated output files
499 # in an output format independent way. The create the layout file that represents
500 # doxygen's defaults, run doxygen with the -l option. You can optionally specify a
501 # file name after the option, if omitted DoxygenLayout.xml will be used as the name
502 # of the layout file.
503
504 LAYOUT_FILE =
505
506 #---------------------------------------------------------------------------
507 # configuration options related to warning and progress messages
508 #---------------------------------------------------------------------------
509
510 # The QUIET tag can be used to turn on/off the messages that are generated
511 # by doxygen. Possible values are YES and NO. If left blank NO is used.
512
513 QUIET = YES
514
515 # The WARNINGS tag can be used to turn on/off the warning messages that are
516 # generated by doxygen. Possible values are YES and NO. If left blank
517 # NO is used.
518
519 WARNINGS = YES
520
521 # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
522 # for undocumented members. If EXTRACT_ALL is set to YES then this flag will
523 # automatically be disabled.
524
525 WARN_IF_UNDOCUMENTED = YES
526
527 # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
528 # potential errors in the documentation, such as not documenting some
529 # parameters in a documented function, or documenting parameters that
530 # don't exist or using markup commands wrongly.
531
532 WARN_IF_DOC_ERROR = YES
533
534 # This WARN_NO_PARAMDOC option can be abled to get warnings for
535 # functions that are documented, but have no documentation for their parameters
536 # or return value. If set to NO (the default) doxygen will only warn about
537 # wrong or incomplete parameter documentation, but not about the absence of
538 # documentation.
539
540 WARN_NO_PARAMDOC = NO
541
542 # The WARN_FORMAT tag determines the format of the warning messages that
543 # doxygen can produce. The string should contain the $file, $line, and $text
544 # tags, which will be replaced by the file and line number from which the
545 # warning originated and the warning text. Optionally the format may contain
546 # $version, which will be replaced by the version of the file (if it could
547 # be obtained via FILE_VERSION_FILTER)
548
549 WARN_FORMAT = "$file:$line: $text"
550
551 # The WARN_LOGFILE tag can be used to specify a file to which warning
552 # and error messages should be written. If left blank the output is written
553 # to stderr.
554
555 WARN_LOGFILE =
556
557 #---------------------------------------------------------------------------
558 # configuration options related to the input files
559 #---------------------------------------------------------------------------
560
561 # The INPUT tag can be used to specify the files and/or directories that contain
562 # documented source files. You may enter file names like "myfile.cpp" or
563 # directories like "/usr/src/myproject". Separate the files or directories
564 # with spaces.
565
566 INPUT = @DOXYFILE_SOURCE_DIRS@
567
568 # This tag can be used to specify the character encoding of the source files
569 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
570 # also the default input encoding. Doxygen uses libiconv (or the iconv built
571 # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
572 # the list of possible encodings.
573
574 INPUT_ENCODING = UTF-8
575
576 # If the value of the INPUT tag contains directories, you can use the
577 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
578 # and *.h) to filter out the source-files in the directories. If left
579 # blank the following patterns are tested:
580 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
581 # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
582
583 FILE_PATTERNS =
584
585 # The RECURSIVE tag can be used to turn specify whether or not subdirectories
586 # should be searched for input files as well. Possible values are YES and NO.
587 # If left blank NO is used.
588
589 RECURSIVE = YES
590
591 # The EXCLUDE tag can be used to specify files and/or directories that should
592 # excluded from the INPUT source files. This way you can easily exclude a
593 # subdirectory from a directory tree whose root is specified with the INPUT tag.
594
595 EXCLUDE = "_darcs"
596
597 # The EXCLUDE_SYMLINKS tag can be used select whether or not files or
598 # directories that are symbolic links (a Unix filesystem feature) are excluded
599 # from the input.
600
601 EXCLUDE_SYMLINKS = NO
602
603 # If the value of the INPUT tag contains directories, you can use the
604 # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
605 # certain files from those directories. Note that the wildcards are matched
606 # against the file with absolute path, so to exclude all test directories
607 # for example use the pattern */test/*
608
609 EXCLUDE_PATTERNS = "*/.*" "*/.*/*"
610
611 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
612 # (namespaces, classes, functions, etc.) that should be excluded from the
613 # output. The symbol name can be a fully qualified name, a word, or if the
614 # wildcard * is used, a substring. Examples: ANamespace, AClass,
615 # AClass::ANamespace, ANamespace::*Test
616
617 EXCLUDE_SYMBOLS =
618
619 # The EXAMPLE_PATH tag can be used to specify one or more files or
620 # directories that contain example code fragments that are included (see
621 # the \include command).
622
623 EXAMPLE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/examples"
624
625 # If the value of the EXAMPLE_PATH tag contains directories, you can use the
626 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
627 # and *.h) to filter out the source-files in the directories. If left
628 # blank all files are included.
629
630 EXAMPLE_PATTERNS =
631
632 # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
633 # searched for input files to be used with the \include or \dontinclude
634 # commands irrespective of the value of the RECURSIVE tag.
635 # Possible values are YES and NO. If left blank NO is used.
636
637 EXAMPLE_RECURSIVE = NO
638
639 # The IMAGE_PATH tag can be used to specify one or more files or
640 # directories that contain image that are included in the documentation (see
641 # the \image command).
642
643 IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@"
644
645 # The INPUT_FILTER tag can be used to specify a program that doxygen should
646 # invoke to filter for each input file. Doxygen will invoke the filter program
647 # by executing (via popen()) the command <filter> <input-file>, where <filter>
648 # is the value of the INPUT_FILTER tag, and <input-file> is the name of an
649 # input file. Doxygen will then use the output that the filter program writes
650 # to standard output.
651 # If FILTER_PATTERNS is specified, this tag will be
652 # ignored.
653
654 INPUT_FILTER =
655
656 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
657 # basis.
658 # Doxygen will compare the file name with each pattern and apply the
659 # filter if there is a match.
660 # The filters are a list of the form:
661 # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
662 # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
663 # is applied to all files.
664
665 FILTER_PATTERNS =
666
667 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
668 # INPUT_FILTER) will be used to filter the input files when producing source
669 # files to browse (i.e. when SOURCE_BROWSER is set to YES).
670
671 FILTER_SOURCE_FILES = NO
672
673 #---------------------------------------------------------------------------
674 # configuration options related to source browsing
675 #---------------------------------------------------------------------------
676
677 # If the SOURCE_BROWSER tag is set to YES then a list of source files will
678 # be generated. Documented entities will be cross-referenced with these sources.
679 # Note: To get rid of all source code in the generated output, make sure also
680 # VERBATIM_HEADERS is set to NO.
681
682 SOURCE_BROWSER = NO
683
684 # Setting the INLINE_SOURCES tag to YES will include the body
685 # of functions and classes directly in the documentation.
686
687 INLINE_SOURCES = NO
688
689 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
690 # doxygen to hide any special comment blocks from generated source code
691 # fragments. Normal C and C++ comments will always remain visible.
692
693 STRIP_CODE_COMMENTS = YES
694
695 # If the REFERENCED_BY_RELATION tag is set to YES
696 # then for each documented function all documented
697 # functions referencing it will be listed.
698
699 REFERENCED_BY_RELATION = NO
700
701 # If the REFERENCES_RELATION tag is set to YES
702 # then for each documented function all documented entities
703 # called/used by that function will be listed.
704
705 REFERENCES_RELATION = NO
706
707 # If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
708 # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
709 # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
710 # link to the source code.
711 # Otherwise they will link to the documentation.
712
713 REFERENCES_LINK_SOURCE = YES
714
715 # If the USE_HTAGS tag is set to YES then the references to source code
716 # will point to the HTML generated by the htags(1) tool instead of doxygen
717 # built-in source browser. The htags tool is part of GNU's global source
718 # tagging system (see http://www.gnu.org/software/global/global.html). You
719 # will need version 4.8.6 or higher.
720
721 USE_HTAGS = NO
722
723 # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
724 # will generate a verbatim copy of the header file for each class for
725 # which an include is specified. Set to NO to disable this.
726
727 VERBATIM_HEADERS = YES
728
729 #---------------------------------------------------------------------------
730 # configuration options related to the alphabetical class index
731 #---------------------------------------------------------------------------
732
733 # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
734 # of all compounds will be generated. Enable this if the project
735 # contains a lot of classes, structs, unions or interfaces.
736
737 ALPHABETICAL_INDEX = NO
738
739 # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
740 # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
741 # in which this list will be split (can be a number in the range [1..20])
742
743 COLS_IN_ALPHA_INDEX = 5
744
745 # In case all classes in a project start with a common prefix, all
746 # classes will be put under the same header in the alphabetical index.
747 # The IGNORE_PREFIX tag can be used to specify one or more prefixes that
748 # should be ignored while generating the index headers.
749
750 IGNORE_PREFIX =
751
752 #---------------------------------------------------------------------------
753 # configuration options related to the HTML output
754 #---------------------------------------------------------------------------
755
756 # If the GENERATE_HTML tag is set to YES (the default) Doxygen will
757 # generate HTML output.
758
759 GENERATE_HTML = YES
760
761 # The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
762 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
763 # put in front of it. If left blank `html' will be used as the default path.
764
765 HTML_OUTPUT = "@DOXYFILE_HTML_DIR@"
766
767 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for
768 # each generated HTML page (for example: .htm,.php,.asp). If it is left blank
769 # doxygen will generate files with .html extension.
770
771 HTML_FILE_EXTENSION = .html
772
773 # The HTML_HEADER tag can be used to specify a personal HTML header for
774 # each generated HTML page. If it is left blank doxygen will generate a
775 # standard header.
776
777 HTML_HEADER =
778
779 # The HTML_FOOTER tag can be used to specify a personal HTML footer for
780 # each generated HTML page. If it is left blank doxygen will generate a
781 # standard footer.
782
783 HTML_FOOTER =
784
785 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
786 # style sheet that is used by each HTML page. It can be used to
787 # fine-tune the look of the HTML output. If the tag is left blank doxygen
788 # will generate a default style sheet. Note that doxygen will try to copy
789 # the style sheet file to the HTML output directory, so don't put your own
790 # stylesheet in the HTML output directory as well, or it will be erased!
791
792 HTML_STYLESHEET =
793
794 # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
795 # files or namespaces will be aligned in HTML using tables. If set to
796 # NO a bullet list will be used.
797
798 HTML_ALIGN_MEMBERS = YES
799
800 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
801 # documentation will contain sections that can be hidden and shown after the
802 # page has loaded. For this to work a browser that supports
803 # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
804 # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
805
806 HTML_DYNAMIC_SECTIONS = NO
807
808 # If the GENERATE_DOCSET tag is set to YES, additional index files
809 # will be generated that can be used as input for Apple's Xcode 3
810 # integrated development environment, introduced with OSX 10.5 (Leopard).
811 # To create a documentation set, doxygen will generate a Makefile in the
812 # HTML output directory. Running make will produce the docset in that
813 # directory and running "make install" will install the docset in
814 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
815 # it at startup.
816 # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
817
818 GENERATE_DOCSET = NO
819
820 # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
821 # feed. A documentation feed provides an umbrella under which multiple
822 # documentation sets from a single provider (such as a company or product suite)
823 # can be grouped.
824
825 DOCSET_FEEDNAME = "Doxygen generated docs"
826
827 # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
828 # should uniquely identify the documentation set bundle. This should be a
829 # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
830 # will append .docset to the name.
831
832 DOCSET_BUNDLE_ID = org.doxygen.Project
833
834 # If the GENERATE_HTMLHELP tag is set to YES, additional index files
835 # will be generated that can be used as input for tools like the
836 # Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
837 # of the generated HTML documentation.
838
839 GENERATE_HTMLHELP = NO
840
841 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
842 # be used to specify the file name of the resulting .chm file. You
843 # can add a path in front of the file if the result should not be
844 # written to the html output directory.
845
846 CHM_FILE =
847
848 # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
849 # be used to specify the location (absolute path including file name) of
850 # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
851 # the HTML help compiler on the generated index.hhp.
852
853 HHC_LOCATION =
854
855 # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
856 # controls if a separate .chi index file is generated (YES) or that
857 # it should be included in the master .chm file (NO).
858
859 GENERATE_CHI = NO
860
861 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
862 # is used to encode HtmlHelp index (hhk), content (hhc) and project file
863 # content.
864
865 CHM_INDEX_ENCODING =
866
867 # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
868 # controls whether a binary table of contents is generated (YES) or a
869 # normal table of contents (NO) in the .chm file.
870
871 BINARY_TOC = NO
872
873 # The TOC_EXPAND flag can be set to YES to add extra items for group members
874 # to the contents of the HTML help documentation and to the tree view.
875
876 TOC_EXPAND = NO
877
878 # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
879 # are set, an additional index file will be generated that can be used as input for
880 # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
881 # HTML documentation.
882
883 GENERATE_QHP = NO
884
885 # If the QHG_LOCATION tag is specified, the QCH_FILE tag can
886 # be used to specify the file name of the resulting .qch file.
887 # The path specified is relative to the HTML output folder.
888
889 QCH_FILE =
890
891 # The QHP_NAMESPACE tag specifies the namespace to use when generating
892 # Qt Help Project output. For more information please see
893 # http://doc.trolltech.com/qthelpproject.html#namespace
894
895 QHP_NAMESPACE =
896
897 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
898 # Qt Help Project output. For more information please see
899 # http://doc.trolltech.com/qthelpproject.html#virtual-folders
900
901 QHP_VIRTUAL_FOLDER = doc
902
903 # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
904 # For more information please see
905 # http://doc.trolltech.com/qthelpproject.html#custom-filters
906
907 QHP_CUST_FILTER_NAME =
908
909 # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
910 # <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
911
912 QHP_CUST_FILTER_ATTRS =
913
914 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
915 # filter section matches.
916 # <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
917
918 QHP_SECT_FILTER_ATTRS =
919
920 # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
921 # be used to specify the location of Qt's qhelpgenerator.
922 # If non-empty doxygen will try to run qhelpgenerator on the generated
923 # .qhp file.
924
925 QHG_LOCATION =
926
927 # The DISABLE_INDEX tag can be used to turn on/off the condensed index at
928 # top of each HTML page. The value NO (the default) enables the index and
929 # the value YES disables it.
930
931 DISABLE_INDEX = NO
932
933 # This tag can be used to set the number of enum values (range [1..20])
934 # that doxygen will group on one line in the generated HTML documentation.
935
936 ENUM_VALUES_PER_LINE = 4
937
938 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
939 # structure should be generated to display hierarchical information.
940 # If the tag value is set to FRAME, a side panel will be generated
941 # containing a tree-like index structure (just like the one that
942 # is generated for HTML Help). For this to work a browser that supports
943 # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
944 # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
945 # probably better off using the HTML help feature. Other possible values
946 # for this tag are: HIERARCHIES, which will generate the Groups, Directories,
947 # and Class Hierarchy pages using a tree view instead of an ordered list;
948 # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
949 # disables this behavior completely. For backwards compatibility with previous
950 # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
951 # respectively.
952
953 GENERATE_TREEVIEW = NONE
954
955 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
956 # used to set the initial width (in pixels) of the frame in which the tree
957 # is shown.
958
959 TREEVIEW_WIDTH = 250
960
961 # Use this tag to change the font size of Latex formulas included
962 # as images in the HTML documentation. The default is 10. Note that
963 # when you change the font size after a successful doxygen run you need
964 # to manually remove any form_*.png images from the HTML output directory
965 # to force them to be regenerated.
966
967 FORMULA_FONTSIZE = 10
968
969 #---------------------------------------------------------------------------
970 # configuration options related to the LaTeX output
971 #---------------------------------------------------------------------------
972
973 # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
974 # generate Latex output.
975
976 GENERATE_LATEX = @DOXYFILE_GENERATE_LATEX@
977
978 # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
979 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
980 # put in front of it. If left blank `latex' will be used as the default path.
981
982 LATEX_OUTPUT = "@DOXYFILE_LATEX_DIR@"
983
984 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
985 # invoked. If left blank `latex' will be used as the default command name.
986
987 LATEX_CMD_NAME = "@LATEX_COMPILER@"
988
989 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
990 # generate index for LaTeX. If left blank `makeindex' will be used as the
991 # default command name.
992
993 MAKEINDEX_CMD_NAME = "@MAKEINDEX_COMPILER@"
994
995 # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
996 # LaTeX documents. This may be useful for small projects and may help to
997 # save some trees in general.
998
999 COMPACT_LATEX = NO
1000
1001 # The PAPER_TYPE tag can be used to set the paper type that is used
1002 # by the printer. Possible values are: a4, a4wide, letter, legal and
1003 # executive. If left blank a4wide will be used.
1004
1005 PAPER_TYPE = a4wide
1006
1007 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
1008 # packages that should be included in the LaTeX output.
1009
1010 EXTRA_PACKAGES =
1011
1012 # The LATEX_HEADER tag can be used to specify a personal LaTeX header for
1013 # the generated latex document. The header should contain everything until
1014 # the first chapter. If it is left blank doxygen will generate a
1015 # standard header. Notice: only use this tag if you know what you are doing!
1016
1017 LATEX_HEADER =
1018
1019 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
1020 # is prepared for conversion to pdf (using ps2pdf). The pdf file will
1021 # contain links (just like the HTML output) instead of page references
1022 # This makes the output suitable for online browsing using a pdf viewer.
1023
1024 PDF_HYPERLINKS = YES
1025
1026 # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
1027 # plain latex in the generated Makefile. Set this option to YES to get a
1028 # higher quality PDF documentation.
1029
1030 USE_PDFLATEX = @DOXYFILE_PDFLATEX@
1031
1032 # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
1033 # command to the generated LaTeX files. This will instruct LaTeX to keep
1034 # running if errors occur, instead of asking the user for help.
1035 # This option is also used when generating formulas in HTML.
1036
1037 LATEX_BATCHMODE = YES
1038
1039 # If LATEX_HIDE_INDICES is set to YES then doxygen will not
1040 # include the index chapters (such as File Index, Compound Index, etc.)
1041 # in the output.
1042
1043 LATEX_HIDE_INDICES = NO
1044
1045 #---------------------------------------------------------------------------
1046 # configuration options related to the RTF output
1047 #---------------------------------------------------------------------------
1048
1049 # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
1050 # The RTF output is optimized for Word 97 and may not look very pretty with
1051 # other RTF readers or editors.
1052
1053 GENERATE_RTF = NO
1054
1055 # The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
1056 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1057 # put in front of it. If left blank `rtf' will be used as the default path.
1058
1059 RTF_OUTPUT = rtf
1060
1061 # If the COMPACT_RTF tag is set to YES Doxygen generates more compact
1062 # RTF documents. This may be useful for small projects and may help to
1063 # save some trees in general.
1064
1065 COMPACT_RTF = NO
1066
1067 # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
1068 # will contain hyperlink fields. The RTF file will
1069 # contain links (just like the HTML output) instead of page references.
1070 # This makes the output suitable for online browsing using WORD or other
1071 # programs which support those fields.
1072 # Note: wordpad (write) and others do not support links.
1073
1074 RTF_HYPERLINKS = NO
1075
1076 # Load stylesheet definitions from file. Syntax is similar to doxygen's
1077 # config file, i.e. a series of assignments. You only have to provide
1078 # replacements, missing definitions are set to their default value.
1079
1080 RTF_STYLESHEET_FILE =
1081
1082 # Set optional variables used in the generation of an rtf document.
1083 # Syntax is similar to doxygen's config file.
1084
1085 RTF_EXTENSIONS_FILE =
1086
1087 #---------------------------------------------------------------------------
1088 # configuration options related to the man page output
1089 #---------------------------------------------------------------------------
1090
1091 # If the GENERATE_MAN tag is set to YES (the default) Doxygen will
1092 # generate man pages
1093
1094 GENERATE_MAN = NO
1095
1096 # The MAN_OUTPUT tag is used to specify where the man pages will be put.
1097 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1098 # put in front of it. If left blank `man' will be used as the default path.
1099
1100 MAN_OUTPUT = man
1101
1102 # The MAN_EXTENSION tag determines the extension that is added to
1103 # the generated man pages (default is the subroutine's section .3)
1104
1105 MAN_EXTENSION = .3
1106
1107 # If the MAN_LINKS tag is set to YES and Doxygen generates man output,
1108 # then it will generate one additional man file for each entity
1109 # documented in the real man page(s). These additional files
1110 # only source the real man page, but without them the man command
1111 # would be unable to find the correct page. The default is NO.
1112
1113 MAN_LINKS = NO
1114
1115 #---------------------------------------------------------------------------
1116 # configuration options related to the XML output
1117 #---------------------------------------------------------------------------
1118
1119 # If the GENERATE_XML tag is set to YES Doxygen will
1120 # generate an XML file that captures the structure of
1121 # the code including all documentation.
1122
1123 GENERATE_XML = NO
1124
1125 # The XML_OUTPUT tag is used to specify where the XML pages will be put.
1126 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1127 # put in front of it. If left blank `xml' will be used as the default path.
1128
1129 XML_OUTPUT = xml
1130
1131 # The XML_SCHEMA tag can be used to specify an XML schema,
1132 # which can be used by a validating XML parser to check the
1133 # syntax of the XML files.
1134
1135 XML_SCHEMA =
1136
1137 # The XML_DTD tag can be used to specify an XML DTD,
1138 # which can be used by a validating XML parser to check the
1139 # syntax of the XML files.
1140
1141 XML_DTD =
1142
1143 # If the XML_PROGRAMLISTING tag is set to YES Doxygen will
1144 # dump the program listings (including syntax highlighting
1145 # and cross-referencing information) to the XML output. Note that
1146 # enabling this will significantly increase the size of the XML output.
1147
1148 XML_PROGRAMLISTING = YES
1149
1150 #---------------------------------------------------------------------------
1151 # configuration options for the AutoGen Definitions output
1152 #---------------------------------------------------------------------------
1153
1154 # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
1155 # generate an AutoGen Definitions (see autogen.sf.net) file
1156 # that captures the structure of the code including all
1157 # documentation. Note that this feature is still experimental
1158 # and incomplete at the moment.
1159
1160 GENERATE_AUTOGEN_DEF = NO
1161
1162 #---------------------------------------------------------------------------
1163 # configuration options related to the Perl module output
1164 #---------------------------------------------------------------------------
1165
1166 # If the GENERATE_PERLMOD tag is set to YES Doxygen will
1167 # generate a Perl module file that captures the structure of
1168 # the code including all documentation. Note that this
1169 # feature is still experimental and incomplete at the
1170 # moment.
1171
1172 GENERATE_PERLMOD = NO
1173
1174 # If the PERLMOD_LATEX tag is set to YES Doxygen will generate
1175 # the necessary Makefile rules, Perl scripts and LaTeX code to be able
1176 # to generate PDF and DVI output from the Perl module output.
1177
1178 PERLMOD_LATEX = NO
1179
1180 # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
1181 # nicely formatted so it can be parsed by a human reader.
1182 # This is useful
1183 # if you want to understand what is going on.
1184 # On the other hand, if this
1185 # tag is set to NO the size of the Perl module output will be much smaller
1186 # and Perl will parse it just the same.
1187
1188 PERLMOD_PRETTY = YES
1189
1190 # The names of the make variables in the generated doxyrules.make file
1191 # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
1192 # This is useful so different doxyrules.make files included by the same
1193 # Makefile don't overwrite each other's variables.
1194
1195 PERLMOD_MAKEVAR_PREFIX =
1196
1197 #---------------------------------------------------------------------------
1198 # Configuration options related to the preprocessor
1199 #---------------------------------------------------------------------------
1200
1201 # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
1202 # evaluate all C-preprocessor directives found in the sources and include
1203 # files.
1204
1205 ENABLE_PREPROCESSING = YES
1206
1207 # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
1208 # names in the source code. If set to NO (the default) only conditional
1209 # compilation will be performed. Macro expansion can be done in a controlled
1210 # way by setting EXPAND_ONLY_PREDEF to YES.
1211
1212 MACRO_EXPANSION = NO
1213
1214 # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
1215 # then the macro expansion is limited to the macros specified with the
1216 # PREDEFINED and EXPAND_AS_DEFINED tags.
1217
1218 EXPAND_ONLY_PREDEF = NO
1219
1220 # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
1221 # in the INCLUDE_PATH (see below) will be search if a #include is found.
1222
1223 SEARCH_INCLUDES = YES
1224
1225 # The INCLUDE_PATH tag can be used to specify one or more directories that
1226 # contain include files that are not input files but should be processed by
1227 # the preprocessor.
1228
1229 INCLUDE_PATH =
1230
1231 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
1232 # patterns (like *.h and *.hpp) to filter out the header-files in the
1233 # directories. If left blank, the patterns specified with FILE_PATTERNS will
1234 # be used.
1235
1236 INCLUDE_FILE_PATTERNS =
1237
1238 # The PREDEFINED tag can be used to specify one or more macro names that
1239 # are defined before the preprocessor is started (similar to the -D option of
1240 # gcc). The argument of the tag is a list of macros of the form: name
1241 # or name=definition (no spaces). If the definition and the = are
1242 # omitted =1 is assumed. To prevent a macro definition from being
1243 # undefined via #undef or recursively expanded use the := operator
1244 # instead of the = operator.
1245
1246 PREDEFINED =
1247
1248 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
1249 # this tag can be used to specify a list of macro names that should be expanded.
1250 # The macro definition that is found in the sources will be used.
1251 # Use the PREDEFINED tag if you want to use a different macro definition.
1252
1253 EXPAND_AS_DEFINED =
1254
1255 # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
1256 # doxygen's preprocessor will remove all function-like macros that are alone
1257 # on a line, have an all uppercase name, and do not end with a semicolon. Such
1258 # function macros are typically used for boiler-plate code, and will confuse
1259 # the parser if not removed.
1260
1261 SKIP_FUNCTION_MACROS = YES
1262
1263 #---------------------------------------------------------------------------
1264 # Configuration::additions related to external references
1265 #---------------------------------------------------------------------------
1266
1267 # The TAGFILES option can be used to specify one or more tagfiles.
1268 # Optionally an initial location of the external documentation
1269 # can be added for each tagfile. The format of a tag file without
1270 # this location is as follows:
1271 #
1272 # TAGFILES = file1 file2 ...
1273 # Adding location for the tag files is done as follows:
1274 #
1275 # TAGFILES = file1=loc1 "file2 = loc2" ...
1276 # where "loc1" and "loc2" can be relative or absolute paths or
1277 # URLs. If a location is present for each tag, the installdox tool
1278 # does not have to be run to correct the links.
1279 # Note that each tag file must have a unique name
1280 # (where the name does NOT include the path)
1281 # If a tag file is not located in the directory in which doxygen
1282 # is run, you must also specify the path to the tagfile here.
1283
1284 TAGFILES =
1285
1286 # When a file name is specified after GENERATE_TAGFILE, doxygen will create
1287 # a tag file that is based on the input files it reads.
1288
1289 GENERATE_TAGFILE =
1290
1291 # If the ALLEXTERNALS tag is set to YES all external classes will be listed
1292 # in the class index. If set to NO only the inherited external classes
1293 # will be listed.
1294
1295 ALLEXTERNALS = NO
1296
1297 # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
1298 # in the modules index. If set to NO, only the current project's groups will
1299 # be listed.
1300
1301 EXTERNAL_GROUPS = YES
1302
1303 # The PERL_PATH should be the absolute path and name of the perl script
1304 # interpreter (i.e. the result of `which perl').
1305
1306 PERL_PATH = /usr/bin/perl
1307
1308 #---------------------------------------------------------------------------
1309 # Configuration options related to the dot tool
1310 #---------------------------------------------------------------------------
1311
1312 # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
1313 # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
1314 # or super classes. Setting the tag to NO turns the diagrams off. Note that
1315 # this option is superseded by the HAVE_DOT option below. This is only a
1316 # fallback. It is recommended to install and use dot, since it yields more
1317 # powerful graphs.
1318
1319 CLASS_DIAGRAMS = YES
1320
1321 # You can define message sequence charts within doxygen comments using the \msc
1322 # command. Doxygen will then run the mscgen tool (see
1323 # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
1324 # documentation. The MSCGEN_PATH tag allows you to specify the directory where
1325 # the mscgen tool resides. If left empty the tool is assumed to be found in the
1326 # default search path.
1327
1328 MSCGEN_PATH =
1329
1330 # If set to YES, the inheritance and collaboration graphs will hide
1331 # inheritance and usage relations if the target is undocumented
1332 # or is not a class.
1333
1334 HIDE_UNDOC_RELATIONS = YES
1335
1336 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
1337 # available from the path. This tool is part of Graphviz, a graph visualization
1338 # toolkit from AT&T and Lucent Bell Labs. The other options in this section
1339 # have no effect if this option is set to NO (the default)
1340
1341 HAVE_DOT = @DOXYFILE_DOT@
1342
1343 # By default doxygen will write a font called FreeSans.ttf to the output
1344 # directory and reference it in all dot files that doxygen generates. This
1345 # font does not include all possible unicode characters however, so when you need
1346 # these (or just want a differently looking font) you can specify the font name
1347 # using DOT_FONTNAME. You need need to make sure dot is able to find the font,
1348 # which can be done by putting it in a standard location or by setting the
1349 # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
1350 # containing the font.
1351
1352 DOT_FONTNAME = FreeSans
1353
1354 # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
1355 # The default size is 10pt.
1356
1357 DOT_FONTSIZE = 10
1358
1359 # By default doxygen will tell dot to use the output directory to look for the
1360 # FreeSans.ttf font (which doxygen will put there itself). If you specify a
1361 # different font using DOT_FONTNAME you can set the path where dot
1362 # can find it using this tag.
1363
1364 DOT_FONTPATH =
1365
1366 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
1367 # will generate a graph for each documented class showing the direct and
1368 # indirect inheritance relations. Setting this tag to YES will force the
1369 # the CLASS_DIAGRAMS tag to NO.
1370
1371 CLASS_GRAPH = YES
1372
1373 # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
1374 # will generate a graph for each documented class showing the direct and
1375 # indirect implementation dependencies (inheritance, containment, and
1376 # class references variables) of the class with other documented classes.
1377
1378 COLLABORATION_GRAPH = YES
1379
1380 # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
1381 # will generate a graph for groups, showing the direct groups dependencies
1382
1383 GROUP_GRAPHS = YES
1384
1385 # If the UML_LOOK tag is set to YES doxygen will generate inheritance and
1386 # collaboration diagrams in a style similar to the OMG's Unified Modeling
1387 # Language.
1388
1389 UML_LOOK = NO
1390
1391 # If set to YES, the inheritance and collaboration graphs will show the
1392 # relations between templates and their instances.
1393
1394 TEMPLATE_RELATIONS = NO
1395
1396 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
1397 # tags are set to YES then doxygen will generate a graph for each documented
1398 # file showing the direct and indirect include dependencies of the file with
1399 # other documented files.
1400
1401 INCLUDE_GRAPH = YES
1402
1403 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
1404 # HAVE_DOT tags are set to YES then doxygen will generate a graph for each
1405 # documented header file showing the documented files that directly or
1406 # indirectly include this file.
1407
1408 INCLUDED_BY_GRAPH = YES
1409
1410 # If the CALL_GRAPH and HAVE_DOT options are set to YES then
1411 # doxygen will generate a call dependency graph for every global function
1412 # or class method. Note that enabling this option will significantly increase
1413 # the time of a run. So in most cases it will be better to enable call graphs
1414 # for selected functions only using the \callgraph command.
1415
1416 CALL_GRAPH = NO
1417
1418 # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
1419 # doxygen will generate a caller dependency graph for every global function
1420 # or class method. Note that enabling this option will significantly increase
1421 # the time of a run. So in most cases it will be better to enable caller
1422 # graphs for selected functions only using the \callergraph command.
1423
1424 CALLER_GRAPH = NO
1425
1426 # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
1427 # will graphical hierarchy of all classes instead of a textual one.
1428
1429 GRAPHICAL_HIERARCHY = YES
1430
1431 # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
1432 # then doxygen will show the dependencies a directory has on other directories
1433 # in a graphical way. The dependency relations are determined by the #include
1434 # relations between the files in the directories.
1435
1436 DIRECTORY_GRAPH = YES
1437
1438 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
1439 # generated by dot. Possible values are png, jpg, or gif
1440 # If left blank png will be used.
1441
1442 DOT_IMAGE_FORMAT = png
1443
1444 # The tag DOT_PATH can be used to specify the path where the dot tool can be
1445 # found. If left blank, it is assumed the dot tool can be found in the path.
1446
1447 DOT_PATH = "@DOXYGEN_DOT_PATH@"
1448
1449 # The DOTFILE_DIRS tag can be used to specify one or more directories that
1450 # contain dot files that are included in the documentation (see the
1451 # \dotfile command).
1452
1453 DOTFILE_DIRS =
1454
1455 # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
1456 # nodes that will be shown in the graph. If the number of nodes in a graph
1457 # becomes larger than this value, doxygen will truncate the graph, which is
1458 # visualized by representing a node as a red box. Note that doxygen if the
1459 # number of direct children of the root node in a graph is already larger than
1460 # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
1461 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
1462
1463 DOT_GRAPH_MAX_NODES = 50
1464
1465 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
1466 # graphs generated by dot. A depth value of 3 means that only nodes reachable
1467 # from the root by following a path via at most 3 edges will be shown. Nodes
1468 # that lay further from the root node will be omitted. Note that setting this
1469 # option to 1 or 2 may greatly reduce the computation time needed for large
1470 # code bases. Also note that the size of a graph can be further restricted by
1471 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
1472
1473 MAX_DOT_GRAPH_DEPTH = 0
1474
1475 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
1476 # background. This is disabled by default, because dot on Windows does not
1477 # seem to support this out of the box. Warning: Depending on the platform used,
1478 # enabling this option may lead to badly anti-aliased labels on the edges of
1479 # a graph (i.e. they become hard to read).
1480
1481 DOT_TRANSPARENT = YES
1482
1483 # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
1484 # files in one run (i.e. multiple -o and -T options on the command line). This
1485 # makes dot run faster, but since only newer versions of dot (>1.8.10)
1486 # support this, this feature is disabled by default.
1487
1488 DOT_MULTI_TARGETS = NO
1489
1490 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
1491 # generate a legend page explaining the meaning of the various boxes and
1492 # arrows in the dot generated graphs.
1493
1494 GENERATE_LEGEND = YES
1495
1496 # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
1497 # remove the intermediate dot files that are used to generate
1498 # the various graphs.
1499
1500 DOT_CLEANUP = YES
1501
1502 #---------------------------------------------------------------------------
1503 # Options related to the search engine
1504 #---------------------------------------------------------------------------
1505
1506 # The SEARCHENGINE tag specifies whether or not a search engine should be
1507 # used. If set to NO the values of all tags below this one will be ignored.
1508
1509 SEARCHENGINE = NO
0 # - Run Doxygen
1 #
2 # Adds a doxygen target that runs doxygen to generate the html
3 # and optionally the LaTeX API documentation.
4 # The doxygen target is added to the doc target as a dependency.
5 # i.e.: the API documentation is built with:
6 # make doc
7 #
8 # USAGE: GLOBAL INSTALL
9 #
10 # Install it with:
11 # cmake ./ && sudo make install
12 # Add the following to the CMakeLists.txt of your project:
13 # include(UseDoxygen OPTIONAL)
14 # Optionally copy Doxyfile.in in the directory of CMakeLists.txt and edit it.
15 #
16 # USAGE: INCLUDE IN PROJECT
17 #
18 # set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
19 # include(UseDoxygen)
20 # Add the Doxyfile.in and UseDoxygen.cmake files to the projects source directory.
21 #
22 #
23 # CONFIGURATION
24 #
25 # To configure Doxygen you can edit Doxyfile.in and set some variables in cmake.
26 # Variables you may define are:
27 # DOXYFILE_SOURCE_DIR - Path where the Doxygen input files are.
28 # Defaults to the current source directory.
29 # DOXYFILE_EXTRA_SOURCES - Additional source diretories/files for Doxygen to scan.
30 # The Paths should be in double quotes and separated by space. e.g.:
31 # "${CMAKE_CURRENT_BINARY_DIR}/foo.c" "${CMAKE_CURRENT_BINARY_DIR}/bar/"
32 #
33 # DOXYFILE_OUTPUT_DIR - Path where the Doxygen output is stored.
34 # Defaults to "${CMAKE_CURRENT_BINARY_DIR}/doc".
35 #
36 # DOXYFILE_LATEX - ON/OFF; Set to "ON" if you want the LaTeX documentation
37 # to be built.
38 # DOXYFILE_LATEX_DIR - Directory relative to DOXYFILE_OUTPUT_DIR where
39 # the Doxygen LaTeX output is stored. Defaults to "latex".
40 #
41 # DOXYFILE_HTML_DIR - Directory relative to DOXYFILE_OUTPUT_DIR where
42 # the Doxygen html output is stored. Defaults to "html".
43 #
44
45 #
46 # Copyright (c) 2009, 2010, 2011 Tobias Rautenkranz <tobias@rautenkranz.ch>
47 #
48 # Redistribution and use is allowed according to the terms of the New
49 # BSD license.
50 # For details see the accompanying COPYING-CMAKE-SCRIPTS file.
51 #
52
53 macro(usedoxygen_set_default name value type docstring)
54 if(NOT DEFINED "${name}")
55 set("${name}" "${value}" CACHE "${type}" "${docstring}")
56 endif()
57 endmacro()
58
59 find_package(Doxygen)
60
61 if(DOXYGEN_FOUND)
62 find_file(DOXYFILE_IN "Doxyfile.in"
63 PATHS "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_ROOT}/Modules/"
64 NO_DEFAULT_PATH
65 DOC "Path to the doxygen configuration template file")
66 set(DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
67 include(FindPackageHandleStandardArgs)
68 find_package_handle_standard_args(DOXYFILE_IN DEFAULT_MSG "DOXYFILE_IN")
69 endif()
70
71 if(DOXYGEN_FOUND AND DOXYFILE_IN_FOUND)
72 usedoxygen_set_default(DOXYFILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc"
73 PATH "Doxygen output directory")
74 usedoxygen_set_default(DOXYFILE_HTML_DIR "html"
75 STRING "Doxygen HTML output directory")
76 usedoxygen_set_default(DOXYFILE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
77 PATH "Input files source directory")
78 usedoxygen_set_default(DOXYFILE_EXTRA_SOURCE_DIRS ""
79 STRING "Additional source files/directories separated by space")
80 set(DOXYFILE_SOURCE_DIRS "\"${DOXYFILE_SOURCE_DIR}\" ${DOXYFILE_EXTRA_SOURCES}")
81
82 usedoxygen_set_default(DOXYFILE_LATEX YES BOOL "Generate LaTeX API documentation" OFF)
83 usedoxygen_set_default(DOXYFILE_LATEX_DIR "latex" STRING "LaTex output directory")
84
85 mark_as_advanced(DOXYFILE_OUTPUT_DIR DOXYFILE_HTML_DIR DOXYFILE_LATEX_DIR
86 DOXYFILE_SOURCE_DIR DOXYFILE_EXTRA_SOURCE_DIRS DOXYFILE_IN)
87
88
89 set_property(DIRECTORY
90 APPEND PROPERTY
91 ADDITIONAL_MAKE_CLEAN_FILES
92 "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_HTML_DIR}")
93
94 add_custom_target(doxygen
95 COMMAND "${DOXYGEN_EXECUTABLE}"
96 "${DOXYFILE}"
97 COMMENT "Writing documentation to ${DOXYFILE_OUTPUT_DIR}..."
98 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
99
100 set(DOXYFILE_DOT "NO")
101 if(DOXYGEN_DOT_EXECUTABLE)
102 set(DOXYFILE_DOT "YES")
103 endif()
104
105 ## LaTeX
106 set(DOXYFILE_PDFLATEX "NO")
107
108 set_property(DIRECTORY APPEND PROPERTY
109 ADDITIONAL_MAKE_CLEAN_FILES
110 "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}")
111
112 if(DOXYFILE_LATEX STREQUAL "ON")
113 set(DOXYFILE_GENERATE_LATEX "YES")
114 find_package(LATEX)
115 find_program(DOXYFILE_MAKE make)
116 mark_as_advanced(DOXYFILE_MAKE)
117 if(LATEX_COMPILER AND MAKEINDEX_COMPILER AND DOXYFILE_MAKE)
118 if(PDFLATEX_COMPILER)
119 set(DOXYFILE_PDFLATEX "YES")
120 endif()
121
122 add_custom_command(TARGET doxygen
123 POST_BUILD
124 COMMAND "${DOXYFILE_MAKE}"
125 COMMENT "Running LaTeX for Doxygen documentation in ${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}..."
126 WORKING_DIRECTORY "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}")
127 else()
128 set(DOXYGEN_LATEX "NO")
129 endif()
130 else()
131 set(DOXYFILE_GENERATE_LATEX "NO")
132 endif()
133
134
135 configure_file("${DOXYFILE_IN}" "${DOXYFILE}" @ONLY)
136
137 get_target_property(DOC_TARGET doc TYPE)
138 if(NOT DOC_TARGET)
139 add_custom_target(doc)
140 endif()
141
142 add_dependencies(doc doxygen)
143 endif()
0 IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
1 MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
2 ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
3
4 FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
5 STRING(REGEX REPLACE "\n" ";" files "${files}")
6 FOREACH(file ${files})
7 MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
8 IF(EXISTS "$ENV{DESTDIR}${file}")
9 EXEC_PROGRAM(
10 "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
11 OUTPUT_VARIABLE rm_out
12 RETURN_VALUE rm_retval
13 )
14 IF(NOT "${rm_retval}" STREQUAL 0)
15 MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
16 ENDIF(NOT "${rm_retval}" STREQUAL 0)
17 ELSE(EXISTS "$ENV{DESTDIR}${file}")
18 MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
19 ENDIF(EXISTS "$ENV{DESTDIR}${file}")
20 ENDFOREACH(file)
0 FILE(MD5 ${check_file} real_sum)
1
2 IF(NOT (${real_sum} STREQUAL ${good_sum}))
3 MESSAGE(FATAL_ERROR "expected ${good_sum}, got ${real_sum}")
4 ENDIF(NOT (${real_sum} STREQUAL ${good_sum}))
0
1 PROJECT(example)
2
3 ADD_EXECUTABLE(rewrite
4 rewrite.cxx)
5
6 TARGET_LINK_LIBRARIES(rewrite
7 Cifti
8 ${LIBS})
9
10 ADD_EXECUTABLE(xmlinfo
11 xmlinfo.cxx)
12
13 TARGET_LINK_LIBRARIES(xmlinfo
14 Cifti
15 ${LIBS})
16
17 INCLUDE_DIRECTORIES(
18 ${CMAKE_SOURCE_DIR}/example
19 ${CMAKE_SOURCE_DIR}/src
20 )
21
22 SET(cifti_files
23 Conte69.MyelinAndCorrThickness.32k_fs_LR.dscalar.nii
24 Conte69.MyelinAndCorrThickness.32k_fs_LR.dtseries.nii
25 Conte69.MyelinAndCorrThickness.32k_fs_LR.ptseries.nii
26 Conte69.parcellations_VGD11b.32k_fs_LR.dlabel.nii
27 ones.dscalar.nii
28 )
29
30 IF(QT_FOUND)
31 #QT4
32 SET(cifti_be_md5s
33 3ba20f6ef590735c1211991f8e0144e6
34 e3a1639ef4b354752b0abb0613eedb73
35 9fd1b6fb7b938155226ca53fdaff5a12
36 48903d80589d294b5421cad9a7ba9264
37 93bb7f76c8d273817601ea5be236f83d
38 )
39 SET(cifti_le_md5s
40 3e24e7908122905c2a2d357347ecee7c
41 4ee31da414dcd26fe37855601ae3e9e2
42 e096a1cf0713f7a36590445e9f6564b1
43 32345267599b07083092b7dedfd8796c
44 512e0359c64d69dde93d605f8797f3a2
45 )
46 ELSE(QT_FOUND)
47 #xml++
48 SET(cifti_be_md5s
49 a2581f76a94b7c4371d6970f1262e1bc
50 ca4cfb02fb7f0d8f167f546aab85e91d
51 892c26e691fe62c4edf358f7f91942a3
52 e761d405c945e724b37f96e356dc19ad
53 2e4efa4e36cdb13f514c265e6c9c8c4b
54 )
55 SET(cifti_le_md5s
56 39ecfba4c1dade1a2ce3e95b325bd8b1
57 921a42e4181052ef4f211d389a2e9328
58 764c816004ebe23d188d98f9f29c7337
59 6fabac021e377efd35dede7198feefd4
60 fe0cbb768e26aa12a0e03990f4f50a30
61 )
62 ENDIF(QT_FOUND)
63
64 #ADD_TEST(timer ${CMAKE_CURRENT_BINARY_DIR}/Tests/test_driver timer)
65
66 LIST(LENGTH cifti_files num_cifti_files)
67 #FOREACH(... RANGE x) is stupid, it does all of [0, x] inclusive, totaling x + 1 iterations
68 MATH(EXPR loop_end "${num_cifti_files} - 1")
69
70 FOREACH(index RANGE ${loop_end})
71 LIST(GET cifti_files ${index} testfile)
72 #ADD_TEST doesn't seem to have a way to capture stdout, so checking the output for consistency seems to be a problem
73 ADD_TEST(info-${testfile} xmlinfo ${CMAKE_SOURCE_DIR}/example/data/${testfile})
74
75 ADD_TEST(rewrite-little-${testfile} rewrite ${CMAKE_SOURCE_DIR}/example/data/${testfile} little-${testfile} LITTLE)
76 LIST(GET cifti_le_md5s ${index} goodsum)
77 ADD_TEST(rewrite-little-md5-${testfile} ${CMAKE_COMMAND} -Dgood_sum=${goodsum} -Dcheck_file=little-${testfile} -P ${CMAKE_SOURCE_DIR}/cmake/scripts/testmd5.cmake)
78
79 ADD_TEST(rewrite-big-${testfile} rewrite ${CMAKE_SOURCE_DIR}/example/data/${testfile} big-${testfile} BIG)
80 LIST(GET cifti_be_md5s ${index} goodsum)
81 ADD_TEST(rewrite-big-md5-${testfile} ${CMAKE_COMMAND} -Dgood_sum=${goodsum} -Dcheck_file=big-${testfile} -P ${CMAKE_SOURCE_DIR}/cmake/scripts/testmd5.cmake)
82 ENDFOREACH(index RANGE ${loop_end})
0 {\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf440
1 {\fonttbl\f0\fswiss\fcharset77 Arial-BoldMS;\f1\fswiss\fcharset77 ArialMS;\f2\froman\fcharset77 TimesNewRomanMS;
2 \f3\froman\fcharset77 TimesNewRomanItMS;\f4\froman\fcharset77 TimesNewRomanBdMS;}
3 {\colortbl;\red255\green255\blue255;}
4 \paperw11899\paperh16839\margl1134\margr1134\margb1134\margt1134\vieww7740\viewh9940\viewkind0
5 \deftab709
6 \pard\pardeftab709\sb240\sa120
7
8 \f0\b\fs32 \cf0 PLEASE READ: \
9 Open Data Commons is not a law firm and does not provide legal services of any kind.\
10 \pard\pardeftab709\sb240\sa120
11
12 \f1\b0 \cf0 Open Data Commons has no formal relationship with you. Your receipt of this document does not create any kind of agent-client relationship.\
13 Please seek the advice of a suitably qualified legal professional licensed to practice in your jurisdiction before using this document.
14 \f0\b \
15 Use of this document.\
16 Do not link to this document as a means of using it for your content. If you choose, after consulting with an appropriate legal professional, to use this document, please host it on your own site or apply it directly to the content you wish to be covered. You do so at your own risk.\
17 No warranties and disclaimer of any damages.\
18
19 \f1\b0 This information is provided \'d4as is\'d4, and this site makes no warranties on the information provided. Any damages resulting from its use are disclaimed.\
20 \pard\pardeftab709\sa120
21
22 \f2\fs24 \cf0 \
23 \
24 \
25 \
26 \
27 \
28 \
29 \
30 \
31 \
32 \
33 *****\
34 \
35 \pard\pardeftab709\sb240\sa120
36
37 \f0\b\fs32 \cf0 Open Data Commons \'d0 Public Domain Dedication & Licence (PDDL)\
38 Preamble\
39 \pard\pardeftab709\ql\qnatural
40
41 \f2\b0\fs24 \cf0 The Open Data Commons \'d0 Public Domain Dedication & Licence is a document intended to allow you to freely share, modify, and use this work for any purpose and without any restrictions. This licence is intended for use on databases or their contents ("data"), either together or individually.\
42 \pard\pardeftab709
43 \cf0 \
44 Many databases are covered by copyright. Some jurisdictions, mainly in Europe, have specific special rights that cover databases called the "sui generis" database right. Both of these sets of rights, as well as other legal rights used to protect databases and data, can create uncertainty or practical difficulty for those wishing to share databases and their underlying data but retain a limited amount of rights under a "some rights reserved" approach to licensing as outlined in the Science Commons Protocol for Implementing Open Access Data. As a result, this waiver and licence tries to the fullest extent possible to eliminate or fully license any rights that cover this database and data. Any
45 \f3\i Community Norms
46 \f2\i0 or similar statements of use of the database or data do not form a part of this document, and do not act as a contract for access or other terms of use for the database or data.\
47 \
48 \pard\pardeftab709\ql\qnatural
49
50 \f4\b \cf0 The position of the recipient of the work\
51 \pard\pardeftab709
52
53 \f2\b0 \cf0 \
54 \pard\pardeftab709\ql\qnatural
55 \cf0 Because this document places the database and its contents in or as close as possible within the public domain, there are no restrictions or requirements placed on the recipient by this document. Recipients may use this work commercially, use technical protection measures, combine this data or database with other databases or data, and share their changes and additions or keep them secret. It is not a requirement that recipients provide further users with a copy of this licence or attribute the original creator of the data or database as a source. The goal is to eliminate restrictions held by the original creator of the data and database on the use of it by others.\
56 \pard\pardeftab709
57 \cf0 \
58 \pard\pardeftab709\ql\qnatural
59
60 \f4\b \cf0 The position of the dedicator of the work\
61 \pard\pardeftab709\ql\qnatural
62
63 \f2\b0 \cf0 \
64 \pard\pardeftab709
65 \cf0 Copyright law, as with most other law under the banner of "intellectual property", is inherently national law. This means that there exists several differences in how copyright and other intellectual property rights can be relinquished, waived or licensed in the many legal jurisdictions of the world. This is despite much harmonisation of minimum levels of protection. The internet and other communication technologies span these many disparate legal jurisdictions and thus pose special difficulties for a document relinquishing and waiving intellectual property rights, including copyright and database rights, for use by the global community. Because of this feature of intellectual property law, this document first relinquishes the rights and waives the relevant rights and claims. It then goes on to license these same rights for jurisdictions or areas of law that may make it difficult to relinquish or waive rights or claims.\
66 \
67 The purpose of this document is to enable rightsholders to place their work into the public domain. Unlike licences for free and open source software, free cultural works, or open content licences, rightsholders will not be able to "dual license" their work by releasing the same work under different licences. This is because they have allowed anyone to use the work in whatever way they choose. Rightsholders therefore can't re-license it under copyright or database rights on different terms because they have nothing left to license. Doing so creates truly accessible data to build rich applications and advance the progress of science and the arts.\
68 \
69 This document can cover either or both of the database and its contents (the data). Because databases can have a wide variety of content \'d0 not just factual data \'d0 rightsholders should use the Open Data Commons \'d0 Public Domain Dedication & Licence for an entire database and its contents only if everything can be placed under the terms of this document. Because even factual data can sometimes have intellectual property rights, rightsholders should use this licence to cover both the database and its factual data when making material available under this document; even if it is likely that the data would not be covered by copyright or database rights. \
70 \
71 Rightsholders can also use this document to cover any copyright or database rights claims over only a database, and leave the contents to be covered by other licences or documents. They can do this because this document refers to the "Work", which can be either \'d0 or both \'d0 the database and its contents. As a result, rightsholders need to clearly state what they are dedicating under this document when they dedicate it.\
72 \
73 Just like any licence or other document dealing with intellectual property, rightsholders should be aware that one can only license what one owns. Please ensure that the rights have been cleared to make this material available under this document.\
74 \
75 This document permanently and irrevocably makes the Work available to the public for any use of any kind, and it should not be used unless the rightsholder is prepared for this to happen. \
76 \pard\pardeftab709\ql\qnatural
77 \cf0 \
78 \pard\pardeftab709\sb240\sa120
79
80 \f0\b\fs32 \cf0 Part I: Introduction\
81 \pard\pardeftab709\sa120
82
83 \f2\b0\fs24 \cf0 \
84 The
85 \f4\b Rightsholder
86 \f2\b0 (the Person holding rights or claims over the Work) agrees as follows: \
87 \pard\pardeftab709\li283\fi-283\ri-6\sb240\sa120\ql\qnatural
88
89 \f4\b\fs32 \cf0 1.0
90 \f0 Definitions of Capitalised Words\
91 \pard\pardeftab709\ql\qnatural
92
93 \f4\fs24 \cf0 \
94 "Copyright"
95 \f2\b0 \'d0 Includes rights under copyright and under neighbouring rights and similarly related sets of rights under the law of the relevant jurisdiction under Section 6.4.\
96 \
97
98 \f4\b "Data"
99 \f2\b0 \'d0 The contents of the Database, which includes the information, independent works, or other material collected into the Database offered under the terms of this Document. \
100 \pard\pardeftab709\sa120
101 \cf0 \
102 \pard\pardeftab709\ql\qnatural
103
104 \f4\b \cf0 "Database"
105 \f2\b0 \'d0 A collection of Data arranged in a systematic or methodical way and individually accessible by electronic or other means offered under the terms of this Document. \
106 \pard\pardeftab709
107 \cf0 \
108 \pard\pardeftab709\ql\qnatural
109
110 \f4\b \cf0 "Database Right"
111 \f2\b0 \'d0 Means rights over Data resulting from the Chapter III ("sui generis") rights in the Database Directive (Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases) and any future updates as well as any similar rights available in the relevant jurisdiction under Section 6.4.
112 \f4\b \
113 \pard\pardeftab709\ql\qnatural
114
115 \f2\b0 \cf0 \
116 \pard\pardeftab709\ql\qnatural
117
118 \f4\b \cf0 "Document"
119 \f2\b0 \'d0 means this relinquishment and waiver of rights and claims and back up licence agreement. \
120 \
121
122 \f4\b "Person"
123 \f2\b0 \'d0 Means a natural or legal person or a body of persons corporate or incorporate.\
124 \pard\pardeftab709
125 \cf0 \
126 \pard\pardeftab709
127
128 \f4\b \cf0 "Use"
129 \f2\b0 \'d0 As a verb, means doing any act that is restricted by Copyright or Database Rights whether in the original medium or any other; and includes modifying the Work as may be technically necessary to use it in a different mode or format. This includes the right to sublicense the Work.\
130 \
131
132 \f4\b "Work"
133 \f2\b0 \'d0 Means either or both of the Database and Data offered under the terms of this Document. \
134 \pard\pardeftab709\ql\qnatural
135
136 \f4\b \cf0 \
137 \pard\pardeftab709
138 \cf0 "You"
139 \f2\b0 \'d0 the Person acquiring rights under the licence elements of this Document.\
140 \pard\pardeftab709\ql\qnatural
141 \cf0 \
142 \pard\pardeftab709\ql\qnatural
143
144 \f4\b \cf0 Words in the singular include the plural and vice versa.\
145 \pard\pardeftab709\li283\fi-283\ri-6\sb240\sa120\ql\qnatural
146
147 \fs32 \cf0 2.0
148 \f0 What this document covers\
149 \pard\pardeftab709\ql\qnatural
150
151 \f2\b0\fs24 \cf0 \
152 \pard\pardeftab709
153 \cf0 2.1.
154 \f4\b Legal effect of this Document.
155 \f2\b0 This Document is:\
156 \pard\pardeftab709\li705\ri-6\ql\qnatural
157 \cf0 \
158 \pard\pardeftab709\li660\fi20
159 \cf0 a. A dedication to the public domain and waiver of Copyright and Database Rights over the Work; and\
160 \pard\pardeftab709\li660\fi20\ql\qnatural
161 \cf0 \
162 \pard\pardeftab709\li660\fi20
163 \cf0 b. A licence of Copyright and Database Rights over the Work in jurisdictions that do not allow for relinquishment or waiver.\
164 \pard\pardeftab709\ql\qnatural
165 \cf0 \
166 2.2.
167 \f4\b Legal rights covered.
168 \f2\b0 \
169 \pard\pardeftab709\li690\ri-6\ql\qnatural
170 \cf0 \
171 a.
172 \f4\b Copyright.
173 \f2\b0 Any copyright or neighbouring rights in the Work. Copyright law varies between jurisdictions, but is likely to cover: the
174 \f3\i Database model or schema
175 \f2\i0 , which is the structure, arrangement, and organisation of the Database, and can also include the Database tables and table indexes; the
176 \f3\i data entry and output sheets
177 \f2\i0 ; and the
178 \f3\i Field names
179 \f2\i0 of Data stored in the Database. Copyright may also cover the Data depending on the jurisdiction and type of Data; and\
180 \pard\pardeftab709
181 \cf0 \
182 \pard\pardeftab709\li690\ri-6\ql\qnatural
183 \cf0 b.
184 \f4\b Database Rights.
185 \f2\b0 Database Rights only extend to the extraction and re-utilisation of the whole or a substantial part of the Data. Database Rights can apply even when there is no copyright over the Database. Database Rights can also apply when the Data is removed from the Database and is selected and arranged in a way that would not infringe any applicable copyright.\
186 \pard\pardeftab709\ql\qnatural
187 \cf0 \
188 2.2
189 \f4\b Rights not covered.
190 \f2\b0 \
191 \pard\pardeftab709
192 \cf0 \
193 \pard\pardeftab709\li690\ri-6\ql\qnatural
194 \cf0 a. This Document does not apply to computer programs used in the making or operation of the Database; \
195 \pard\pardeftab709\li700
196 \cf0 \
197 b. This Document does not cover any patents over the Data or the Database. Please see Section 4.2 later in this Document for further details; and\
198 \
199 c. This Document does not cover any trade marks associated with the Database. Please see Section 4.3 later in this Document for further details.\
200 \
201 \pard\pardeftab709\ql\qnatural
202 \cf0 Users of this Database are cautioned that they may have to clear other rights or consult other licences.\
203 \pard\pardeftab709
204 \cf0 \
205 2.3
206 \f4\b Facts are free.
207 \f2\b0 The Rightsholder takes the position that factual information is not covered by Copyright. This Document however covers the Work in jurisdictions that may protect the factual information in the Work by Copyright, and to cover any information protected by Copyright that is contained in the Work.\
208 \pard\pardeftab709\sb240\sa120
209
210 \f0\b\fs32 \cf0 Part II: Dedication to the public domain\
211 \pard\pardeftab709\li283\fi-283\ri-6\sb240\sa120\ql\qnatural
212
213 \f4 \cf0 3.0
214 \f0 Dedication, waiver, and licence of Copyright and Database Rights\
215 \pard\pardeftab709\ql\qnatural
216
217 \f2\b0\fs24 \cf0 \
218 \pard\pardeftab709
219 \cf0 3.1
220 \f4\b Dedication of Copyright and Database Rights to the public domain.
221 \f2\b0 The Rightsholder by using this Document, dedicates the Work to the public domain for the benefit of the public and relinquishes all rights in Copyright and Database Rights over the Work.\
222 \
223 \pard\pardeftab709\li630\ri-6\ql\qnatural
224 \cf0 a. The Rightsholder realises that once these rights are relinquished, that the Rightsholder has no further rights in Copyright and Database Rights over the Work, and that the Work is free and open for others to Use.\
225 \pard\pardeftab709
226 \cf0 \
227 b. The Rightsholder intends for their relinquishment to cover all present and future rights in the Work under Copyright and Database Rights, whether they are vested or contingent rights, and that this relinquishment of rights covers all their heirs and successors.\
228 \pard\pardeftab709\ql\qnatural
229 \cf0 \
230 \pard\pardeftab709
231 \cf0 The above relinquishment of rights applies worldwide and includes media and formats now known or created in the future.\
232 \
233 3.2
234 \f4\b Waiver of rights and claims in Copyright and Database Rights when Section 3.1 dedication inapplicable.
235 \f2\b0 If the dedication in Section 3.1 does not apply in the relevant jurisdiction under Section 6.4, the Rightsholder waives any rights and claims that the Rightsholder may have or acquire in the future over the Work in:\
236 \
237 \pard\pardeftab709\li630\ri-6\ql\qnatural
238 \cf0 a. Copyright; and\
239 \pard\pardeftab709
240 \cf0 \
241 \pard\pardeftab709\li560\fi-20
242 \cf0 b. Database Rights.\
243 \pard\pardeftab709\ql\qnatural
244 \cf0 \
245 \pard\pardeftab709
246 \cf0 To the extent possible in the relevant jurisdiction, the above waiver of rights and claims applies worldwide and includes media and formats now known or created in the future. The Rightsholder agrees not to assert the above rights and waives the right to enforce them over the Work. \
247 \
248 3.3
249 \f4\b Licence of Copyright and Database Rights when Sections 3.1 and 3.2 inapplicable.
250 \f2\b0 If the dedication and waiver in Sections 3.1 and 3.2 does not apply in the relevant jurisdiction under Section 6.4, the Rightsholder and You agree as follows:\
251 \
252 \pard\pardeftab709\li630\ri-6\ql\qnatural
253 \cf0 a. The Licensor grants to You a worldwide, royalty-free, non-exclusive, licence to Use the Work for the duration of any applicable Copyright and Database Rights. These rights explicitly include commercial use, and do not exclude any field of endeavour. To the extent possible in the relevant jurisdiction, these rights may be exercised in all media and formats whether now known or created in the future.\
254 \pard\pardeftab709\ql\qnatural
255 \cf0 \
256 3.4
257 \f4\b Moral rights.
258 \f2\b0 This section covers moral rights, including the right to be identified as the author of the Work or to object to treatment that would otherwise prejudice the author's honour and reputation, or any other derogatory treatment:\
259 \pard\pardeftab709\sa120
260 \cf0 \
261 \pard\pardeftab709\li615\ri-6\ql\qnatural
262 \cf0 a. For jurisdictions allowing waiver of moral rights, Licensor waives all moral rights that Licensor may have in the Work to the fullest extent possible by the law of the relevant jurisdiction under Section 6.4; \
263 \pard\pardeftab709\li560\fi-40\sa120
264 \cf0 \
265 b. If waiver of moral rights under Section 3.4 a in the relevant jurisdiction is not possible, Licensor agrees not to assert any moral rights over the Work and waives all claims in moral rights to the fullest extent possible by the law of the relevant jurisdiction under Section 6.4; and\
266 \
267 \pard\pardeftab709\li615\ri-6\ql\qnatural
268 \cf0 c. For jurisdictions not allowing waiver or an agreement not to assert moral rights under Section 3.4 a and b, the author may retain their moral rights over the copyrighted aspects of the Work.\
269 \pard\pardeftab709\ql\qnatural
270 \cf0 \
271 \pard\pardeftab709
272 \cf0 Please note that some jurisdictions do not allow for the waiver of moral rights, and so moral rights may still subsist over the work in some jurisdictions.\
273 \
274 \pard\pardeftab709\li283\fi-283\ri-6\sb240\sa120\ql\qnatural
275
276 \f4\b\fs32 \cf0 4.0
277 \f0 Relationship to other rights\
278 \pard\pardeftab709\ql\qnatural
279
280 \f2\b0\fs24 \cf0 \
281 4.1
282 \f4\b No other contractual conditions.
283 \f2\b0 The Rightsholder makes this Work available to You without any other contractual obligations, either express or implied. Any
284 \f3\i Community Norms
285 \f2\i0 statement associated with the Work is not a contract and does not form part of this Document.\
286 \
287 \pard\pardeftab709
288 \cf0 4.2
289 \f4\b Relationship to patents.
290 \f2\b0 This Document does not grant You a licence for any patents that the Rightsholder may own. Users of this Database are cautioned that they may have to clear other rights or consult other licences.\
291 \pard\pardeftab709\li675\ri-6\ql\qnatural
292 \cf0 \
293 \pard\pardeftab709\ql\qnatural
294 \cf0 4.3
295 \f4\b Relationship to trade marks.
296 \f2\b0 This Document does not grant You a licence for any trade marks that the Rightsholder may own or that the Rightsholder may use to cover the Work. Users of this Database are cautioned that they may have to clear other rights or consult other licences.\
297 \
298 \pard\pardeftab709\sb240\sa120
299
300 \f0\b\fs32 \cf0 Part III: General provisions\
301 \pard\pardeftab709\li283\fi-283\ri-6\sb240\sa120\ql\qnatural
302
303 \f4 \cf0 5.0
304 \f0 Warranties, disclaimer, and limitation of liability\
305 \pard\pardeftab709\ql\qnatural
306
307 \f2\b0\fs24 \cf0 5.1 The Work is provided by the Rightsholder "as is" and without any warranty of any kind, either express or implied, whether of title, of accuracy or completeness, of the presence of absence of errors, of fitness for purpose, or otherwise. Some jurisdictions do not allow the exclusion of implied warranties, so this exclusion may not apply to You.\
308 \pard\pardeftab709
309 \cf0 \
310 5.2 Subject to any liability that may not be excluded or limited by law, the Rightsholder is not \
311 liable for, and expressly excludes, all liability for loss or damage however and whenever caused to anyone by any use under this Document, whether by You or by anyone else, and whether caused by any fault on the part of the Rightsholder or not. This exclusion of liability includes, but is not limited to, any special, incidental, consequential, punitive, or exemplary damages. This exclusion applies even if the Rightsholder has been advised of the possibility of such damages.\
312 \
313 5.3 If liability may not be excluded by law, it is limited to actual and direct financial loss to the extent it is caused by proved negligence on the part of the Rightsholder.\
314 \pard\pardeftab709\li283\fi-283\ri-6\sb240\sa120\ql\qnatural
315
316 \f4\b\fs32 \cf0 6.0
317 \f0 General\
318 \pard\pardeftab709\ql\qnatural
319
320 \f2\b0\fs24 \cf0 \
321 \pard\pardeftab709
322 \cf0 6.1 If any provision of this Document is held to be invalid or unenforceable, that must not affect the validity or enforceability of the remainder of the terms of this Document. \
323 \
324 6.2 This Document is the entire agreement between the parties with respect to the Work covered here. It replaces any earlier understandings, agreements or representations with respect to the Work not specified here. \
325 \
326 \pard\pardeftab709\ql\qnatural
327 \cf0 6.3 This Document does not affect any rights that You or anyone else may independently have under any applicable law to make any use of this Work, including (for jurisdictions where this Document is a licence) fair dealing, fair use, database exceptions, or any other legally recognised limitation or exception to infringement of copyright or other applicable laws. \
328 \
329 6.4 This Document takes effect in the relevant jurisdiction in which the Document terms are sought to be enforced. If the rights waived or granted under applicable law in the relevant jurisdiction includes additional rights not waived or granted under this Document, these additional rights are included in this Document in order to meet the intent of this Document.\
330 }
0 These files are intended for testing CIFTI-2 reading code, identical to the ones in the v1.x zip files from https://www.nitrc.org/projects/cifti/. There are 5 CIFTI-2 files, the other data files that were in the zip have been removed to save space, as they were in different (but related) formats.
1
2 The files:
3
4 Conte69.MyelinAndCorrThickness.32k_fs_LR.dscalar.nii
5 Conte69.MyelinAndCorrThickness.32k_fs_LR.dtseries.nii
6
7 contain the same data, which are two fairly smooth maps, with somewhat different value distributions (MyelinMap_BC_decurv ranges from 1 to 2, while corrThickness ranges from 1 to 4.8). If you see a pattern that alternates from one vertex to the next, you have probably read the data matrix incorrectly, which may happen if you previously read data from CIFTI-1, and used the same code. The CIFTI-2 matrix data should be read as a standard NIFTI-2 file, it does not require the special treatment that CIFTI-1 needed.
8
9 The "ones.dscalar.nii" is the only cifti file in the zip that contains voxel data. All of the data values are 1. The various volume components should align well with subcortical structures in "Conte69_AverageT1w_restore.nii.gz" from any of the v1.x zip files from https://www.nitrc.org/projects/cifti/.
10
11 The files:
12
13 Conte69.MyelinAndCorrThickness.32k_fs_LR.ptseries.nii
14 Conte69.parcellations_VGD11b.32k_fs_LR.dlabel.nii
15
16 should have the first map of the .dlabel.nii file align with the areas in the .ptseries.nii file that have values. The values in the .ptseries.nii file should match the vertex averages over the regions they cover, from the same-index map in "Conte69.MyelinAndCorrThickness.32k_fs_LR.dtseries.nii".
17
18 These files are provided under the Open Data Commons Public Domain Dedication and Licence (PDDL), see ODC_PDDL.rtf and/or http://opendatacommons.org/licenses/pddl/1.0/ for details.
0 #include "CiftiFile.h"
1
2 #include <iostream>
3 #include <vector>
4
5 using namespace std;
6 using namespace cifti;
7
8 /**\file rewrite.cxx
9 This program reads a Cifti file from argv[1], and writes it out to argv[2] with a second CiftiFile object.
10 It uses on-disk reading and writing, so DO NOT have both filenames point to the same file,
11 CiftiFile truncates without any warning when told to write to an existing file.
12
13 \include rewrite.cxx
14 */
15
16 int main(int argc, char** argv)
17 {
18 if (argc < 3)
19 {
20 cout << "usage: " << argv[0] << " <input cifti> <output cifti> [<endian>]" << endl;
21 cout << " rewrite the input cifti file to the output filename." << endl;
22 cout << " endian can be 'LITTLE' or 'BIG', and uses native endianness if not specified" << endl;
23 return 1;
24 }
25 CiftiFile::ENDIAN myEndian = CiftiFile::NATIVE;
26 if (argc > 3)
27 {
28 if (AString(argv[3]) == "LITTLE")
29 {
30 myEndian = CiftiFile::LITTLE;
31 } else if (AString(argv[3]) == "BIG") {
32 myEndian = CiftiFile::BIG;
33 } else {
34 cerr << "unrecognized endianness string: " << argv[3] << endl;
35 return 1;
36 }
37 }
38 try
39 {
40 CiftiFile inputFile(argv[1]);//on-disk reading by default
41 //inputFile.convertToInMemory();//if you want to read it into memory first
42 CiftiFile outputFile;
43 outputFile.setWritingFile(argv[2], CiftiVersion(), myEndian);//sets up on-disk writing with default writing version, from CiftiVersion's default constructor
44 outputFile.setCiftiXML(inputFile.getCiftiXML());//the CiftiXML is how you access all the mapping information
45 const vector<int64_t>& dims = inputFile.getDimensions();
46 vector<float> scratchRow(dims[0]);//read/write a row at a time
47 for (MultiDimIterator<int64_t> iter = inputFile.getIteratorOverRows(); !iter.atEnd(); ++iter)
48 {//helper class to iterate over 2D and 3D cifti with the same code - the "+ 1" is to drop the first dimension (row length)
49 inputFile.getRow(scratchRow.data(), *iter);
50 outputFile.setRow(scratchRow.data(), *iter);
51 }
52 outputFile.writeFile(argv[2]);//because we called setWritingFile with this filename (and default cifti version), this will return immediately
53 //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
54 //it will also rewrite it if you specify an endianness other than ANY (which is the default), and the endianness doesn't match what it was already written as (via setWritingFile)
55 } catch (CiftiException& e) {
56 cerr << "Caught CiftiException: " + AString_to_std_string(e.whatString()) << endl;
57 return 1;
58 }
59 return 0;
60 }
0 #include "CiftiFile.h"
1
2 #include <iostream>
3 #include <vector>
4
5 using namespace std;
6 using namespace cifti;
7
8 /**\file xmlinfo.cxx
9 This program reads a Cifti file from argv[1], and prints out a summary of the XML.
10
11 \include xmlinfo.cxx
12 */
13
14 int main(int argc, char** argv)
15 {
16 if (argc < 2)
17 {
18 cout << "this program requires one argument: an input cifti file" << endl;
19 return 1;
20 }
21 try
22 {
23 CiftiFile inputFile(argv[1]);//on-disk reading by default, and we only need the XML header anyway
24 const CiftiXML& myXML = inputFile.getCiftiXML();
25 for (int whichDim = 0; whichDim < myXML.getNumberOfDimensions(); ++whichDim)
26 {
27 cout << "Dimension " << whichDim << ": ";
28 switch (myXML.getMappingType(whichDim))
29 {
30 case CiftiMappingType::BRAIN_MODELS:
31 {
32 const CiftiBrainModelsMap& myMap = myXML.getBrainModelsMap(whichDim);
33 cout << "Brain Models, length " << myMap.getLength() << endl;
34 vector<CiftiBrainModelsMap::ModelInfo> myInfo = myMap.getModelInfo();//this is only summary info, same order as the models are in the cifti indices
35 for (int i = 0; i < (int)myInfo.size(); ++i)//to get the lists of vertices/voxels for a model, see getSurfaceMap, getVolumeStructureMap, and getFullVolumeMap
36 {
37 switch (myInfo[i].m_type)
38 {
39 case CiftiBrainModelsMap::SURFACE:
40 cout << " Surface " << AString_to_std_string(StructureEnum::toName(myInfo[i].m_structure)) << ": ";
41 cout << myInfo[i].m_indexCount << " out of " << myMap.getSurfaceNumberOfNodes(myInfo[i].m_structure) << " vertices" << endl;
42 break;
43 case CiftiBrainModelsMap::VOXELS:
44 cout << " Voxels " << AString_to_std_string(StructureEnum::toName(myInfo[i].m_structure)) << ": ";
45 cout << myInfo[i].m_indexCount << " voxels" << endl;
46 break;
47 }
48 }
49 break;
50 }
51 case CiftiMappingType::LABELS:
52 {
53 const CiftiLabelsMap& myMap = myXML.getLabelsMap(whichDim);
54 cout << "Labels, length " << myMap.getLength() << endl;
55 for (int i = 0; i < myMap.getLength(); ++i)
56 {
57 cout << " Index " << i << ": " << AString_to_std_string(myMap.getMapName(i)) << endl;
58 }
59 break;
60 }
61 case CiftiMappingType::PARCELS:
62 {
63 const CiftiParcelsMap& myMap = myXML.getParcelsMap(whichDim);
64 cout << "Parcels, length " << myMap.getLength() << endl;
65 const vector<CiftiParcelsMap::Parcel>& myParcels = myMap.getParcels();
66 for (int i = 0; i < (int)myParcels.size(); ++i)
67 {
68 cout << " Index " << i << ", name '" << AString_to_std_string(myParcels[i].m_name) << "': ";
69 int numVerts = 0;
70 for (map<StructureEnum::Enum, set<int64_t> >::const_iterator iter = myParcels[i].m_surfaceNodes.begin(); iter != myParcels[i].m_surfaceNodes.end(); ++iter)
71 {
72 numVerts += iter->second.size();
73 }
74 cout << numVerts << " vertices, " << myParcels[i].m_voxelIndices.size() << " voxels" << endl;
75 }
76 break;
77 }
78 case CiftiMappingType::SCALARS:
79 {
80 const CiftiScalarsMap& myMap = myXML.getScalarsMap(whichDim);
81 cout << "Scalars, length " << myMap.getLength() << endl;
82 for (int i = 0; i < myMap.getLength(); ++i)
83 {
84 cout << " Index " << i << ": " << AString_to_std_string(myMap.getMapName(i)) << endl;
85 }
86 break;
87 }
88 case CiftiMappingType::SERIES:
89 {
90 const CiftiSeriesMap& myMap = myXML.getSeriesMap(whichDim);
91 cout << "Series, length " << myMap.getLength() << endl;
92 cout << " Start: " << myMap.getStart() << endl;
93 cout << " Step: " << myMap.getStep() << endl;
94 cout << " Unit: " << AString_to_std_string(CiftiSeriesMap::unitToString(myMap.getUnit())) << endl;
95 break;
96 }
97 }
98 cout << endl;//extra line break between dimensions
99 }
100 } catch (CiftiException& e) {
101 cerr << "Caught CiftiException: " + AString_to_std_string(e.whatString()) << endl;
102 return 1;
103 }
104 return 0;
105 }
0 /**
1 \mainpage CiftiLib
2
3 CiftiLib is a library for the CIFTI file format, as documented here: http://www.nitrc.org/projects/cifti/
4
5 It builds with either QT, or libxml++ and libboost-filesystem.
6
7 To use it, include CiftiFile.h, and use the CiftiFile class to read and write cifti files.
8
9 Reading example:
10
11 \code
12 CiftiFile myFile(filename);//defaults to reading data on demand
13 const CiftiXML& myXML = myFile.getCiftiXML();//mapping and dimension information
14 vector<float> rowData(myXML.getDimensionLength(CiftiXML::ALONG_ROW));//allocate array for row data
15 myFile.getRow(rowData.data(), 0);//read the first row
16 \endcode
17
18 Writing example:
19
20 \code
21 CiftiXML myXML;//first, need to set up the dimension mappings
22 CiftiScalarsMap exampleMap;
23 exampleMap.setLength(40);//just as an example
24 myXML.setNumberOfDimensions(2);//2D matrix
25 myXML.setMap(CiftiXML::ALONG_ROW, exampleMap);//set the mappings
26 myXML.setMap(CiftiXML::ALONG_COLUMN, exampleMap);
27 CiftiFile myFile;
28 myFile.setWritingFile(filename);//optional: write rows as they are set
29 myFile.setCiftiXML(myXML);//set the file's mappings
30 myFile.setRow(somedata, 0);//write a row
31 ...
32 myFile.writeFile(filename);//if filename matches what was given to setWritingFile, this does nothing
33 \endcode
34
35 See rewrite.cxx for a program that does a row-by-row copy that works on cifti of any dimensionality.
36
37 See xmlinfo.cxx for a program that prints XML summary information of all dimensions of a cifti file.
38
39 */
0 PROJECT(src)
1
2 SET(HEADERS
3 CiftiFile.h
4 NiftiIO.h
5 )
6
7 SET(SOURCES
8 CiftiFile.cxx
9 NiftiIO.cxx
10 )
11
12 #only the headers that should end up in the top level of CiftiLib when installed
13 #do not move this below the append_subdir_files calls
14 SET(PUBLIC_HEADERS
15 ${HEADERS}
16 )
17
18 #list the subdirectories here, so each build system file only references one additional level
19 SET(PRIVATE_DIRS
20 Cifti
21 Common
22 Nifti
23 )
24
25 ADD_SUBDIRECTORY(Cifti)
26 ADD_SUBDIRECTORY(Common)
27 ADD_SUBDIRECTORY(Nifti)
28
29 macro(append_subdir_files variable dirname)
30 get_directory_property(holder DIRECTORY ${dirname} DEFINITION ${variable})
31 foreach(depfile ${holder})
32 list(APPEND ${variable} "${dirname}/${depfile}")
33 endforeach()
34 endmacro()
35
36 append_subdir_files(SOURCES Nifti)
37 append_subdir_files(HEADERS Nifti)
38 append_subdir_files(SOURCES Cifti)
39 append_subdir_files(HEADERS Cifti)
40 append_subdir_files(SOURCES Common)
41 append_subdir_files(HEADERS Common)
0
1 PROJECT(Cifti)
2
3 SET(HEADERS
4 CiftiXML.h
5 CiftiVersion.h
6
7 CiftiMappingType.h
8 CiftiBrainModelsMap.h
9 CiftiLabelsMap.h
10 CiftiParcelsMap.h
11 CiftiScalarsMap.h
12 CiftiSeriesMap.h
13
14 Label.h
15 LabelTable.h
16 MetaData.h
17
18 StructureEnum.h
19 VolumeSpace.h
20 )
21
22 SET(SOURCES
23 CiftiXML.cxx
24 CiftiVersion.cxx
25
26 CiftiMappingType.cxx
27 CiftiBrainModelsMap.cxx
28 CiftiLabelsMap.cxx
29 CiftiParcelsMap.cxx
30 CiftiScalarsMap.cxx
31 CiftiSeriesMap.cxx
32
33 Label.cxx
34 LabelTable.cxx
35 MetaData.cxx
36
37 StructureEnum.cxx
38 VolumeSpace.cxx
39 )
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiBrainModelsMap.h"
28
29 #include "Common/CiftiException.h"
30
31 #include <algorithm>
32
33 using namespace std;
34 using namespace cifti;
35
36 void CiftiBrainModelsMap::addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const float* roi)
37 {
38 vector<int64_t> tempVector;//pass-through to the other addSurfaceModel after converting roi to vector of indices
39 tempVector.reserve(numberOfNodes);//to make it allocate only once
40 for (int64_t i = 0; i < numberOfNodes; ++i)
41 {
42 if (roi == NULL || roi[i] > 0.0f)
43 {
44 tempVector.push_back(i);
45 }
46 }
47 addSurfaceModel(numberOfNodes, structure, tempVector);
48 }
49
50 void CiftiBrainModelsMap::addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const vector<int64_t>& nodeList)
51 {
52 if (m_surfUsed.find(structure) != m_surfUsed.end())
53 {
54 throw CiftiException("surface structures cannot be repeated in a brain models map");
55 }
56 BrainModelPriv myModel;
57 myModel.m_type = SURFACE;
58 myModel.m_brainStructure = structure;
59 myModel.m_surfaceNumberOfNodes = numberOfNodes;
60 myModel.m_nodeIndices = nodeList;
61 myModel.setupSurface(getNextStart());//do internal setup - also does error checking
62 m_modelsInfo.push_back(myModel);
63 m_surfUsed[structure] = m_modelsInfo.size() - 1;
64 }
65
66 void CiftiBrainModelsMap::BrainModelPriv::setupSurface(const int64_t& start)
67 {
68 if (m_surfaceNumberOfNodes < 1)
69 {
70 throw CiftiException("surface must have at least 1 vertex");
71 }
72 m_modelStart = start;
73 int64_t listSize = (int64_t)m_nodeIndices.size();
74 if (listSize == 0)
75 {
76 throw CiftiException("vertex list must have nonzero length");//NOTE: technically not required by Cifti-1, would need some rewriting to support
77 }
78 m_modelEnd = start + listSize;//one after last
79 vector<bool> used(m_surfaceNumberOfNodes, false);
80 m_nodeToIndexLookup = vector<int64_t>(m_surfaceNumberOfNodes, -1);//reset all to -1 to start
81 for (int64_t i = 0; i < listSize; ++i)
82 {
83 if (m_nodeIndices[i] < 0)
84 {
85 throw CiftiException("vertex list contains negative index");
86 }
87 if (m_nodeIndices[i] >= m_surfaceNumberOfNodes)
88 {
89 throw CiftiException("vertex list contains an index that don't exist in the surface");
90 }
91 if (used[m_nodeIndices[i]])
92 {
93 throw CiftiException("vertex list contains reused index");
94 }
95 used[m_nodeIndices[i]] = true;
96 m_nodeToIndexLookup[m_nodeIndices[i]] = start + i;
97 }
98 }
99
100 void CiftiBrainModelsMap::addVolumeModel(const StructureEnum::Enum& structure, const vector<int64_t>& ijkList)
101 {
102 if (m_volUsed.find(structure) != m_volUsed.end())
103 {
104 throw CiftiException("volume structures cannot be repeated in a brain models map");
105 }
106 int64_t listSize = (int64_t)ijkList.size();
107 if (listSize == 0)
108 {
109 throw CiftiException("voxel list must have nonzero length");//NOTE: technically not required by Cifti-1, would need some rewriting to support
110 }
111 if (listSize % 3 != 0)
112 {
113 throw CiftiException("voxel list must have a length that is a multiple of 3");
114 }
115 int64_t numElems = listSize / 3;
116 const int64_t* dims = NULL;
117 if (!m_ignoreVolSpace)
118 {
119 if (!m_haveVolumeSpace)
120 {
121 throw CiftiException("you must set the volume space before adding volume models");
122 }
123 dims = m_volSpace.getDims();
124 }
125 Compact3DLookup<std::pair<int64_t, StructureEnum::Enum> > tempLookup = m_voxelToIndexLookup;//a copy of the lookup should be faster than other methods of checking for overlap and repeat
126 int64_t nextStart = getNextStart();
127 for (int64_t index = 0; index < numElems; ++index)//do all error checking before adding to lookup
128 {
129 int64_t index3 = index * 3;
130 if (ijkList[index3] < 0 || ijkList[index3 + 1] < 0 || ijkList[index3 + 2] < 0)
131 {
132 throw CiftiException("found negative index in voxel list");
133 }
134 if (!m_ignoreVolSpace && (ijkList[index3] >= dims[0] ||
135 ijkList[index3 + 1] >= dims[1] ||
136 ijkList[index3 + 2] >= dims[2]))
137 {
138 throw CiftiException("found invalid index triple in voxel list: (" + AString_number(ijkList[index3]) + ", "
139 + AString_number(ijkList[index3 + 1]) + ", " + AString_number(ijkList[index3 + 2]) + ")");
140 }
141 if (tempLookup.find(ijkList[index3], ijkList[index3 + 1], ijkList[index3 + 2]) != NULL)
142 {
143 throw CiftiException("volume models may not reuse voxels, either internally or from other structures");
144 }
145 tempLookup.at(ijkList[index3], ijkList[index3 + 1], ijkList[index3 + 2]) = pair<int64_t, StructureEnum::Enum>(nextStart + index, structure);
146 }
147 m_voxelToIndexLookup = tempLookup;
148 BrainModelPriv myModel;
149 myModel.m_type = VOXELS;
150 myModel.m_brainStructure = structure;
151 myModel.m_voxelIndicesIJK = ijkList;
152 myModel.m_modelStart = nextStart;
153 myModel.m_modelEnd = nextStart + numElems;//one after last
154 m_modelsInfo.push_back(myModel);
155 m_volUsed[structure] = m_modelsInfo.size() - 1;
156 }
157
158 void CiftiBrainModelsMap::clear()
159 {
160 m_modelsInfo.clear();
161 m_haveVolumeSpace = false;
162 m_ignoreVolSpace = false;
163 m_voxelToIndexLookup.clear();
164 m_surfUsed.clear();
165 m_volUsed.clear();
166 }
167
168 int64_t CiftiBrainModelsMap::getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const
169 {
170 CiftiAssert(node >= 0);
171 map<StructureEnum::Enum, int>::const_iterator iter = m_surfUsed.find(structure);
172 if (iter == m_surfUsed.end())
173 {
174 return -1;
175 }
176 CiftiAssertVectorIndex(m_modelsInfo, iter->second);
177 const BrainModelPriv& myModel = m_modelsInfo[iter->second];
178 if (node >= myModel.m_surfaceNumberOfNodes) return -1;
179 CiftiAssertVectorIndex(myModel.m_nodeToIndexLookup, node);
180 return myModel.m_nodeToIndexLookup[node];
181 }
182
183 int64_t CiftiBrainModelsMap::getIndexForVoxel(const int64_t* ijk, StructureEnum::Enum* structureOut) const
184 {
185 return getIndexForVoxel(ijk[0], ijk[1], ijk[2], structureOut);
186 }
187
188 int64_t CiftiBrainModelsMap::getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k, StructureEnum::Enum* structureOut) const
189 {
190 const pair<int64_t, StructureEnum::Enum>* iter = m_voxelToIndexLookup.find(i, j, k);//the lookup tolerates weirdness like negatives
191 if (iter == NULL) return -1;
192 if (structureOut != NULL) *structureOut = iter->second;
193 return iter->first;
194 }
195
196 CiftiBrainModelsMap::IndexInfo CiftiBrainModelsMap::getInfoForIndex(const int64_t index) const
197 {
198 CiftiAssert(index >= 0 && index < getLength());
199 IndexInfo ret;
200 int numModels = (int)m_modelsInfo.size();
201 int low = 0, high = numModels - 1;//bisection search
202 while (low != high)
203 {
204 int guess = (low + high) / 2;
205 if (m_modelsInfo[guess].m_modelEnd > index)//modelEnd is 1 after last valid index, equal to next start if there is a next
206 {
207 if (m_modelsInfo[guess].m_modelStart > index)
208 {
209 high = guess - 1;
210 } else {
211 high = guess;
212 low = guess;
213 }
214 } else {
215 low = guess + 1;
216 }
217 }
218 CiftiAssert(index >= m_modelsInfo[low].m_modelStart && index < m_modelsInfo[low].m_modelEnd);//otherwise we have a broken invariant
219 ret.m_structure = m_modelsInfo[low].m_brainStructure;
220 ret.m_type = m_modelsInfo[low].m_type;
221 if (ret.m_type == SURFACE)
222 {
223 ret.m_surfaceNode = m_modelsInfo[low].m_nodeIndices[index - m_modelsInfo[low].m_modelStart];
224 } else {
225 int64_t baseIndex = 3 * (index - m_modelsInfo[low].m_modelStart);
226 ret.m_ijk[0] = m_modelsInfo[low].m_voxelIndicesIJK[baseIndex];
227 ret.m_ijk[1] = m_modelsInfo[low].m_voxelIndicesIJK[baseIndex + 1];
228 ret.m_ijk[2] = m_modelsInfo[low].m_voxelIndicesIJK[baseIndex + 2];
229 }
230 return ret;
231 }
232
233 int64_t CiftiBrainModelsMap::getLength() const
234 {
235 return getNextStart();
236 }
237
238 vector<CiftiBrainModelsMap::ModelInfo> CiftiBrainModelsMap::getModelInfo() const
239 {
240 vector<ModelInfo> ret;
241 int numModels = (int)m_modelsInfo.size();
242 ret.resize(numModels);
243 for (int i = 0; i < numModels; ++i)
244 {
245 ret[i].m_structure = m_modelsInfo[i].m_brainStructure;
246 ret[i].m_type = m_modelsInfo[i].m_type;
247 ret[i].m_indexStart = m_modelsInfo[i].m_modelStart;
248 ret[i].m_indexCount = m_modelsInfo[i].m_modelEnd - m_modelsInfo[i].m_modelStart;
249 }
250 return ret;
251 }
252
253 int64_t CiftiBrainModelsMap::getNextStart() const
254 {
255 if (m_modelsInfo.size() == 0) return 0;
256 return m_modelsInfo.back().m_modelEnd;//NOTE: the models are sorted by their index range, so this works
257 }
258
259 const vector<int64_t>& CiftiBrainModelsMap::getNodeList(const StructureEnum::Enum& structure) const
260 {
261 map<StructureEnum::Enum, int>::const_iterator iter = m_surfUsed.find(structure);
262 if (iter == m_surfUsed.end())
263 {
264 throw CiftiException("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
265 }
266 CiftiAssertVectorIndex(m_modelsInfo, iter->second);
267 return m_modelsInfo[iter->second].m_nodeIndices;
268 }
269
270 vector<CiftiBrainModelsMap::SurfaceMap> CiftiBrainModelsMap::getSurfaceMap(const StructureEnum::Enum& structure) const
271 {
272 vector<SurfaceMap> ret;
273 map<StructureEnum::Enum, int>::const_iterator iter = m_surfUsed.find(structure);
274 if (iter == m_surfUsed.end())
275 {
276 throw CiftiException("getSurfaceMap called for nonexistant structure");//also throw, for consistency
277 }
278 CiftiAssertVectorIndex(m_modelsInfo, iter->second);
279 const BrainModelPriv& myModel = m_modelsInfo[iter->second];
280 int64_t numUsed = (int64_t)myModel.m_nodeIndices.size();
281 ret.resize(numUsed);
282 for (int64_t i = 0; i < numUsed; ++i)
283 {
284 ret[i].m_ciftiIndex = myModel.m_modelStart + i;
285 ret[i].m_surfaceNode = myModel.m_nodeIndices[i];
286 }
287 return ret;
288 }
289
290 int64_t CiftiBrainModelsMap::getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const
291 {
292 map<StructureEnum::Enum, int>::const_iterator iter = m_surfUsed.find(structure);
293 if (iter == m_surfUsed.end())
294 {
295 return -1;
296 }
297 CiftiAssertVectorIndex(m_modelsInfo, iter->second);
298 const BrainModelPriv& myModel = m_modelsInfo[iter->second];
299 return myModel.m_surfaceNumberOfNodes;
300 }
301
302 vector<StructureEnum::Enum> CiftiBrainModelsMap::getSurfaceStructureList() const
303 {
304 vector<StructureEnum::Enum> ret;
305 ret.reserve(m_surfUsed.size());//we can use this to tell us how many there are, but it has reordered them
306 int numModels = (int)m_modelsInfo.size();
307 for (int i = 0; i < numModels; ++i)//we need them in the order they occur in
308 {
309 if (m_modelsInfo[i].m_type == SURFACE)
310 {
311 ret.push_back(m_modelsInfo[i].m_brainStructure);
312 }
313 }
314 return ret;
315 }
316
317 bool CiftiBrainModelsMap::hasSurfaceData(const StructureEnum::Enum& structure) const
318 {
319 map<StructureEnum::Enum, int>::const_iterator iter = m_surfUsed.find(structure);
320 return (iter != m_surfUsed.end());
321 }
322
323 vector<CiftiBrainModelsMap::VolumeMap> CiftiBrainModelsMap::getFullVolumeMap() const
324 {
325 vector<VolumeMap> ret;
326 int numModels = (int)m_modelsInfo.size();
327 for (int i = 0; i < numModels; ++i)
328 {
329 if (m_modelsInfo[i].m_type == VOXELS)
330 {
331 const BrainModelPriv& myModel = m_modelsInfo[i];
332 int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size();
333 CiftiAssert(listSize % 3 == 0);
334 int64_t numUsed = listSize / 3;
335 if (ret.size() == 0) ret.reserve(numUsed);//keep it from doing multiple expansion copies on the first model
336 for (int64_t i = 0; i < numUsed; ++i)
337 {
338 int64_t i3 = i * 3;
339 VolumeMap temp;
340 temp.m_ciftiIndex = myModel.m_modelStart + i;
341 temp.m_ijk[0] = myModel.m_voxelIndicesIJK[i3];
342 temp.m_ijk[1] = myModel.m_voxelIndicesIJK[i3 + 1];
343 temp.m_ijk[2] = myModel.m_voxelIndicesIJK[i3 + 2];
344 ret.push_back(temp);
345 }
346 }
347 }
348 return ret;
349 }
350
351 const VolumeSpace& CiftiBrainModelsMap::getVolumeSpace() const
352 {
353 CiftiAssert(!m_ignoreVolSpace);
354 if (!m_haveVolumeSpace)
355 {
356 throw CiftiException("getVolumeSpace called when no volume space exists");
357 }
358 return m_volSpace;
359 }
360
361 vector<StructureEnum::Enum> CiftiBrainModelsMap::getVolumeStructureList() const
362 {
363 vector<StructureEnum::Enum> ret;
364 ret.reserve(m_volUsed.size());//we can use this to tell us how many there are, but it has reordered them
365 int numModels = (int)m_modelsInfo.size();
366 for (int i = 0; i < numModels; ++i)//we need them in the order they occur in
367 {
368 if (m_modelsInfo[i].m_type == VOXELS)
369 {
370 ret.push_back(m_modelsInfo[i].m_brainStructure);
371 }
372 }
373 return ret;
374 }
375
376 vector<CiftiBrainModelsMap::VolumeMap> CiftiBrainModelsMap::getVolumeStructureMap(const StructureEnum::Enum& structure) const
377 {
378 vector<VolumeMap> ret;
379 map<StructureEnum::Enum, int>::const_iterator iter = m_volUsed.find(structure);
380 if (iter == m_volUsed.end())
381 {
382 throw CiftiException("getVolumeStructureMap called for nonexistant structure");//also throw, for consistency
383 }
384 CiftiAssertVectorIndex(m_modelsInfo, iter->second);
385 const BrainModelPriv& myModel = m_modelsInfo[iter->second];
386 int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size();
387 CiftiAssert(listSize % 3 == 0);
388 int64_t numUsed = listSize / 3;
389 ret.resize(numUsed);
390 for (int64_t i = 0; i < numUsed; ++i)
391 {
392 int64_t i3 = i * 3;
393 ret[i].m_ciftiIndex = myModel.m_modelStart + i;
394 ret[i].m_ijk[0] = myModel.m_voxelIndicesIJK[i3];
395 ret[i].m_ijk[1] = myModel.m_voxelIndicesIJK[i3 + 1];
396 ret[i].m_ijk[2] = myModel.m_voxelIndicesIJK[i3 + 2];
397 }
398 return ret;
399 }
400
401 const vector<int64_t>& CiftiBrainModelsMap::getVoxelList(const StructureEnum::Enum& structure) const
402 {
403 map<StructureEnum::Enum, int>::const_iterator iter = m_volUsed.find(structure);
404 if (iter == m_volUsed.end())
405 {
406 throw CiftiException("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
407 }
408 CiftiAssertVectorIndex(m_modelsInfo, iter->second);
409 return m_modelsInfo[iter->second].m_voxelIndicesIJK;
410 }
411
412 bool CiftiBrainModelsMap::hasVolumeData() const
413 {
414 return (m_volUsed.size() != 0);
415 }
416
417 bool CiftiBrainModelsMap::hasVolumeData(const StructureEnum::Enum& structure) const
418 {
419 map<StructureEnum::Enum, int>::const_iterator iter = m_volUsed.find(structure);
420 return (iter != m_volUsed.end());
421 }
422
423 void CiftiBrainModelsMap::setVolumeSpace(const VolumeSpace& space)
424 {
425 for (map<StructureEnum::Enum, int>::const_iterator iter = m_volUsed.begin(); iter != m_volUsed.end(); ++iter)//the main time this loop isn't empty is parsing cifti-1
426 {
427 CiftiAssertVectorIndex(m_modelsInfo, iter->second);
428 const BrainModelPriv& myModel = m_modelsInfo[iter->second];
429 int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size();
430 CiftiAssert(listSize % 3 == 0);
431 for (int64_t i3 = 0; i3 < listSize; i3 += 3)
432 {
433 if (!space.indexValid(myModel.m_voxelIndicesIJK[i3], myModel.m_voxelIndicesIJK[i3 + 1], myModel.m_voxelIndicesIJK[i3 + 2]))
434 {
435 throw CiftiException("invalid voxel found for volume space");
436 }
437 }
438 }
439 m_ignoreVolSpace = false;
440 m_haveVolumeSpace = true;
441 m_volSpace = space;
442 }
443
444 bool CiftiBrainModelsMap::operator==(const CiftiMappingType& rhs) const
445 {
446 if (rhs.getType() != getType()) return false;
447 const CiftiBrainModelsMap& myrhs = dynamic_cast<const CiftiBrainModelsMap&>(rhs);
448 CiftiAssert(!m_ignoreVolSpace && !myrhs.m_ignoreVolSpace);//these should only be true while in the process of parsing cifti-1, never otherwise
449 if (m_haveVolumeSpace != myrhs.m_haveVolumeSpace) return false;
450 if (m_haveVolumeSpace && (m_volSpace != myrhs.m_volSpace)) return false;
451 return (m_modelsInfo == myrhs.m_modelsInfo);//NOTE: these are sorted by index range, so this works
452 }
453
454 bool CiftiBrainModelsMap::approximateMatch(const CiftiMappingType& rhs, AString* explanation) const
455 {
456 if (rhs.getType() != getType())
457 {
458 if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType());
459 return false;
460 }
461 const CiftiBrainModelsMap& myrhs = dynamic_cast<const CiftiBrainModelsMap&>(rhs);//there is no user-specified metadata, but we want informative messages, so copy and modify the code from ==
462 CiftiAssert(!m_ignoreVolSpace && !myrhs.m_ignoreVolSpace);//these should only be true while in the process of parsing cifti-1, never otherwise
463 if (m_haveVolumeSpace != myrhs.m_haveVolumeSpace)
464 {
465 if (explanation != NULL) *explanation = "one of the mappings has no volume data";
466 return false;
467 }
468 if (m_haveVolumeSpace && (m_volSpace != myrhs.m_volSpace))
469 {
470 if (explanation != NULL) *explanation = "mappings have a different volume space";
471 return false;
472 }
473 if (m_modelsInfo != myrhs.m_modelsInfo)
474 {
475 if (explanation != NULL) *explanation = "mappings include different brainordinates";
476 return false;
477 }
478 return true;
479 }
480
481 bool CiftiBrainModelsMap::BrainModelPriv::operator==(const BrainModelPriv& rhs) const
482 {
483 if (m_brainStructure != rhs.m_brainStructure) return false;
484 if (m_type != rhs.m_type) return false;
485 if (m_modelStart != rhs.m_modelStart) return false;
486 if (m_modelEnd != rhs.m_modelEnd) return false;
487 if (m_type == SURFACE)
488 {
489 if (m_surfaceNumberOfNodes != rhs.m_surfaceNumberOfNodes) return false;
490 int64_t listSize = (int64_t)m_nodeIndices.size();
491 CiftiAssert((int64_t)rhs.m_nodeIndices.size() == listSize);//this should already be checked by start/end above
492 for (int64_t i = 0; i < listSize; ++i)
493 {
494 if (m_nodeIndices[i] != rhs.m_nodeIndices[i]) return false;
495 }
496 } else {
497 int64_t listSize = (int64_t)m_voxelIndicesIJK.size();
498 CiftiAssert((int64_t)rhs.m_voxelIndicesIJK.size() == listSize);//this should already be checked by start/end above
499 for (int64_t i = 0; i < listSize; ++i)
500 {
501 if (m_voxelIndicesIJK[i] != rhs.m_voxelIndicesIJK[i]) return false;
502 }
503 }
504 return true;
505 }
506
507 void CiftiBrainModelsMap::readXML1(XmlReader& xml)
508 {
509 clear();
510 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
511 vector<ParseHelperModel> parsedModels;
512 #ifdef CIFTILIB_USE_QT
513 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
514 {
515 switch (xml.tokenType())
516 {
517 case QXmlStreamReader::StartElement:
518 {
519 QStringRef name = xml.name();
520 if (name != "BrainModel")
521 {
522 throw CiftiException("unexpected element in brain models map: " + name.toString());
523 }
524 ParseHelperModel thisModel;
525 thisModel.parseBrainModel1(xml);
526 if (xml.hasError()) return;
527 parsedModels.push_back(thisModel);
528 break;//the readNext in the for will remove the BrainModel end element
529 }
530 default:
531 break;
532 }
533 }
534 if (xml.hasError()) return;
535 #else
536 #ifdef CIFTILIB_USE_XMLPP
537 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
538 while(!done && xml.read())
539 {
540 switch (xml.get_node_type())
541 {
542 case XmlReader::Element:
543 {
544 AString name = xml.get_local_name();
545 if (name == "BrainModel")
546 {
547 ParseHelperModel thisModel;
548 thisModel.parseBrainModel1(xml);
549 parsedModels.push_back(thisModel);
550 } else {
551 throw CiftiException("unexpected element in brain models map: " + name);
552 }
553 break;
554 }
555 case XmlReader::EndElement:
556 done = true;
557 break;
558 default:
559 break;
560 }
561 }
562 #else
563 #error "not implemented"
564 #endif
565 #endif
566 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
567 sort(parsedModels.begin(), parsedModels.end());
568 int64_t numModels = (int64_t)parsedModels.size();//because we haven't checked them for unique values of BrainStructure yet...yeah, its paranoid
569 int64_t curOffset = 0;
570 for (int64_t i = 0; i < numModels; ++i)
571 {
572 if (parsedModels[i].m_offset != curOffset)
573 {
574 if (parsedModels[i].m_offset < curOffset)
575 {
576 throw CiftiException("models overlap at index " + AString_number(parsedModels[i].m_offset) + ", model " + AString_number(i));
577 } else {
578 throw CiftiException("index " + AString_number(curOffset) + " is not assigned to any model");
579 }
580 }
581 curOffset += parsedModels[i].m_count;
582 }
583 for (int64_t i = 0; i < numModels; ++i)
584 {
585 if (parsedModels[i].m_type == SURFACE)
586 {
587 addSurfaceModel(parsedModels[i].m_surfaceNumberOfNodes,
588 parsedModels[i].m_brainStructure,
589 parsedModels[i].m_nodeIndices);
590 } else {
591 addVolumeModel(parsedModels[i].m_brainStructure,
592 parsedModels[i].m_voxelIndicesIJK);
593 }
594 }
595 m_ignoreVolSpace = false;//in case there are no voxels, but some will be added later
596 }
597
598 void CiftiBrainModelsMap::readXML2(XmlReader& xml)
599 {
600 clear();
601 vector<ParseHelperModel> parsedModels;
602 #ifdef CIFTILIB_USE_QT
603 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
604 {
605 switch (xml.tokenType())
606 {
607 case QXmlStreamReader::StartElement:
608 {
609 QStringRef name = xml.name();
610 if (name == "BrainModel")
611 {
612 ParseHelperModel thisModel;
613 thisModel.parseBrainModel2(xml);
614 if (xml.hasError()) break;
615 parsedModels.push_back(thisModel);
616 } else if (name == "Volume") {
617 if (m_haveVolumeSpace)
618 {
619 throw CiftiException("Volume specified more than once in Brain Models mapping type");
620 } else {
621 m_volSpace.readCiftiXML2(xml);
622 if (xml.hasError()) return;
623 m_haveVolumeSpace = true;
624 }
625 } else {
626 throw CiftiException("unexpected element in brain models map: " + name.toString());
627 }
628 break;//the readNext in the for will remove the BrainModel or Volume end element
629 }
630 default:
631 break;
632 }
633 }
634 if (xml.hasError()) return;
635 #else
636 #ifdef CIFTILIB_USE_XMLPP
637 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
638 while(!done && xml.read())
639 {
640 switch (xml.get_node_type())
641 {
642 case XmlReader::Element:
643 {
644 AString name = xml.get_local_name();
645 if (name == "BrainModel")
646 {
647 ParseHelperModel thisModel;
648 thisModel.parseBrainModel2(xml);
649 parsedModels.push_back(thisModel);
650 } else if (name == "Volume") {
651 if (m_haveVolumeSpace)
652 {
653 throw CiftiException("Volume specified more than once in Brain Models mapping type");
654 } else {
655 m_volSpace.readCiftiXML2(xml);
656 m_haveVolumeSpace = true;
657 }
658 } else {
659 throw CiftiException("unexpected element in brain models map: " + name);
660 }
661 break;
662 }
663 case XmlReader::EndElement:
664 done = true;
665 break;
666 default:
667 break;
668 }
669 }
670 #else
671 #error "not implemented"
672 #endif
673 #endif
674 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
675 sort(parsedModels.begin(), parsedModels.end());
676 int64_t numModels = (int64_t)parsedModels.size();//because we haven't checked them for unique values of BrainStructure yet...yeah, its paranoid
677 int64_t curOffset = 0;
678 for (int64_t i = 0; i < numModels; ++i)
679 {
680 if (parsedModels[i].m_offset != curOffset)
681 {
682 if (parsedModels[i].m_offset < curOffset)
683 {
684 throw CiftiException("models overlap at index " + AString_number(parsedModels[i].m_offset) + ", model " + AString_number(i));
685 } else {
686 throw CiftiException("index " + AString_number(curOffset) + " is not assigned to any model");
687 }
688 }
689 curOffset += parsedModels[i].m_count;
690 }
691 for (int64_t i = 0; i < numModels; ++i)
692 {
693 if (parsedModels[i].m_type == SURFACE)
694 {
695 addSurfaceModel(parsedModels[i].m_surfaceNumberOfNodes,
696 parsedModels[i].m_brainStructure,
697 parsedModels[i].m_nodeIndices);
698 } else {
699 addVolumeModel(parsedModels[i].m_brainStructure,
700 parsedModels[i].m_voxelIndicesIJK);
701 }
702 }
703 }
704
705 void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel1(XmlReader& xml)
706 {
707 vector<AString> mandAttrs(4), optAttrs(1, "SurfaceNumberOfNodes");
708 mandAttrs[0] = "ModelType";
709 mandAttrs[1] = "BrainStructure";
710 mandAttrs[2] = "IndexOffset";
711 mandAttrs[3] = "IndexCount";
712 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs, optAttrs);
713 if (myAttrs.mandatoryVals[0] == "CIFTI_MODEL_TYPE_SURFACE")
714 {
715 m_type = SURFACE;
716 } else if (myAttrs.mandatoryVals[0] == "CIFTI_MODEL_TYPE_VOXELS") {
717 m_type = VOXELS;
718 } else {
719 throw CiftiException("invalid value for ModelType: " + myAttrs.mandatoryVals[0]);
720 }
721 bool ok = false;
722 m_brainStructure = StructureEnum::fromCiftiName(myAttrs.mandatoryVals[1], &ok);
723 if (!ok)
724 {
725 throw CiftiException("invalid value for BrainStructure: " + myAttrs.mandatoryVals[1]);
726 }
727 m_offset = AString_toInt(myAttrs.mandatoryVals[2], ok);
728 if (!ok || m_offset < 0)
729 {
730 throw CiftiException("IndexOffset must be a non-negative integer");
731 }
732 m_count = AString_toInt(myAttrs.mandatoryVals[3], ok);
733 if (!ok || m_count < 1)//NOTE: not technically required by cifti-1, would need some rewriting to support empty brainmodels
734 {
735 throw CiftiException("IndexCount must be a positive integer");
736 }
737 if (m_type == SURFACE)
738 {
739 if (!myAttrs.optionalVals[0].present)//actually conditionally required, not optional
740 {
741 throw CiftiException("BrainModel missing required attribute SurfaceNumberOfNodes");
742 }
743 m_surfaceNumberOfNodes = AString_toInt(myAttrs.optionalVals[0].value, ok);
744 if (!ok || m_surfaceNumberOfNodes < 1)
745 {
746 throw CiftiException("SurfaceNumberOfNodes must be a positive integer");
747 }
748 #ifdef CIFTILIB_USE_QT
749 if (!xml.readNextStartElement())//special case in cifti-1
750 {
751 m_nodeIndices.resize(m_count);
752 for (int64_t i = 0; i < m_count; ++i)
753 {
754 m_nodeIndices[i] = i;
755 }
756 } else {
757 if (xml.name() != "NodeIndices")
758 {
759 throw CiftiException("unexpected element in BrainModel of SURFACE type: " + xml.name().toString());
760 }
761 m_nodeIndices = readIndexArray(xml);
762 xml.readNext();//remove the end element of NodeIndices
763 }
764 if (xml.hasError()) return;
765 #else
766 #ifdef CIFTILIB_USE_XMLPP
767 bool haveNodeIndices = false, done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
768 while(!done && xml.read())
769 {
770 switch (xml.get_node_type())
771 {
772 case XmlReader::Element:
773 {
774 AString name = xml.get_local_name();
775 if (name == "NodeIndices")
776 {
777 if (haveNodeIndices)
778 {
779 throw CiftiException("NodeIndices element may only be specified once");
780 }
781 m_nodeIndices = readIndexArray(xml);
782 haveNodeIndices = true;
783 } else {
784 throw CiftiException("unexpected element in BrainModel: " + name);
785 }
786 break;
787 }
788 case XmlReader::EndElement:
789 done = true;
790 break;
791 default:
792 break;
793 }
794 }
795 if (!haveNodeIndices)//special case in cifti-1
796 {
797 m_nodeIndices.resize(m_count);
798 for (int64_t i = 0; i < m_count; ++i)
799 {
800 m_nodeIndices[i] = i;
801 }
802 }
803 #else
804 #error "not implemented"
805 #endif
806 #endif
807 if ((int64_t)m_nodeIndices.size() != m_count)
808 {
809 throw CiftiException("number of vertex indices does not match IndexCount");
810 }
811 } else {
812 #ifdef CIFTILIB_USE_QT
813 if (!xml.readNextStartElement())
814 {
815 throw CiftiException("BrainModel requires a child element");
816 }
817 if (xml.name() != "VoxelIndicesIJK")
818 {
819 throw CiftiException("unexpected element in BrainModel of VOXELS type: " + xml.name().toString());
820 }
821 m_voxelIndicesIJK = readIndexArray(xml);
822 if (xml.hasError()) return;
823 xml.readNext();//remove the end element of VoxelIndicesIJK
824 #else
825 #ifdef CIFTILIB_USE_XMLPP
826 bool haveVoxelIndices = false, done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
827 while(!done && xml.read())
828 {
829 switch (xml.get_node_type())
830 {
831 case XmlReader::Element:
832 {
833 AString name = xml.get_local_name();
834 if (name == "VoxelIndicesIJK")
835 {
836 if (haveVoxelIndices)
837 {
838 throw CiftiException("VoxelIndicesIJK element may only be specified once");
839 }
840 m_voxelIndicesIJK = readIndexArray(xml);
841 haveVoxelIndices = true;
842 } else {
843 throw CiftiException("unexpected element in BrainModel: " + name);
844 }
845 break;
846 }
847 case XmlReader::EndElement:
848 done = true;
849 break;
850 default:
851 break;
852 }
853 }
854 if (!haveVoxelIndices)//special case in cifti-1
855 {
856 throw CiftiException("BrainModel requires a child element");
857 }
858 #else
859 #error "not implemented"
860 #endif
861 #endif
862 if (m_voxelIndicesIJK.size() % 3 != 0)
863 {
864 throw CiftiException("number of voxel indices is not a multiple of 3");
865 }
866 if ((int64_t)m_voxelIndicesIJK.size() != m_count * 3)
867 {
868 throw CiftiException("number of voxel indices does not match IndexCount");
869 }
870 }
871 #ifdef CIFTILIB_USE_QT
872 while (!xml.atEnd() && !xml.isEndElement())//locate the end element of BrainModel
873 {
874 switch(xml.readNext())
875 {
876 case QXmlStreamReader::StartElement:
877 throw CiftiException("unexpected second element in BrainModel: " + xml.name().toString());
878 default:
879 break;
880 }
881 }
882 #endif
883 CiftiAssert(XmlReader_checkEndElement(xml, "BrainModel"));
884 }
885
886 void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel2(XmlReader& xml)
887 {
888 vector<AString> mandAttrs(4), optAttrs(1, "SurfaceNumberOfVertices");
889 mandAttrs[0] = "ModelType";
890 mandAttrs[1] = "BrainStructure";
891 mandAttrs[2] = "IndexOffset";
892 mandAttrs[3] = "IndexCount";
893 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs, optAttrs);
894 if (myAttrs.mandatoryVals[0] == "CIFTI_MODEL_TYPE_SURFACE")
895 {
896 m_type = SURFACE;
897 } else if (myAttrs.mandatoryVals[0] == "CIFTI_MODEL_TYPE_VOXELS") {
898 m_type = VOXELS;
899 } else {
900 throw CiftiException("invalid value for ModelType: " + myAttrs.mandatoryVals[0]);
901 }
902 bool ok = false;
903 m_brainStructure = StructureEnum::fromCiftiName(myAttrs.mandatoryVals[1], &ok);
904 if (!ok)
905 {
906 throw CiftiException("invalid value for BrainStructure: " + myAttrs.mandatoryVals[1]);
907 }
908 m_offset = AString_toInt(myAttrs.mandatoryVals[2], ok);
909 if (!ok || m_offset < 0)
910 {
911 throw CiftiException("IndexOffset must be a non-negative integer");
912 }
913 m_count = AString_toInt(myAttrs.mandatoryVals[3], ok);
914 if (!ok || m_count < 1)
915 {
916 throw CiftiException("IndexCount must be a positive integer");
917 }
918 if (m_type == SURFACE)
919 {
920 if (!myAttrs.optionalVals[0].present)//actually conditionally required, not optional
921 {
922 throw CiftiException("BrainModel missing required attribute SurfaceNumberOfVertices");
923 }
924 m_surfaceNumberOfNodes = AString_toInt(myAttrs.optionalVals[0].value, ok);
925 if (!ok || m_surfaceNumberOfNodes < 1)
926 {
927 throw CiftiException("SurfaceNumberOfVertices must be a positive integer");
928 }
929 #ifdef CIFTILIB_USE_QT
930 if (!xml.readNextStartElement())
931 {
932 throw CiftiException("BrainModel requires a child element");
933 }
934 if (xml.name() != "VertexIndices")
935 {
936 throw CiftiException("unexpected element in BrainModel of SURFACE type: " + xml.name().toString());
937 }
938 m_nodeIndices = readIndexArray(xml);
939 xml.readNext();//remove the end element of NodeIndices
940 if (xml.hasError()) return;
941 #else
942 #ifdef CIFTILIB_USE_XMLPP
943 bool haveVertexIndices = false, done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
944 while(!done && xml.read())
945 {
946 switch (xml.get_node_type())
947 {
948 case XmlReader::Element:
949 {
950 AString name = xml.get_local_name();
951 if (name == "VertexIndices")
952 {
953 if (haveVertexIndices)
954 {
955 throw CiftiException("VertexIndices element may only be specified once");
956 }
957 m_nodeIndices = readIndexArray(xml);
958 haveVertexIndices = true;
959 } else {
960 throw CiftiException("unexpected element in BrainModel: " + name);
961 }
962 break;
963 }
964 case XmlReader::EndElement:
965 done = true;
966 break;
967 default:
968 break;
969 }
970 }
971 if (!haveVertexIndices) throw CiftiException("VertexIndices element is missing");
972 #else
973 #error "not implemented"
974 #endif
975 #endif
976 if ((int64_t)m_nodeIndices.size() != m_count)
977 {
978 throw CiftiException("number of vertex indices does not match IndexCount");
979 }
980 } else {
981 #ifdef CIFTILIB_USE_QT
982 if (!xml.readNextStartElement())
983 {
984 throw CiftiException("BrainModel requires a child element");
985 }
986 if (xml.name() != "VoxelIndicesIJK")
987 {
988 throw CiftiException("unexpected element in BrainModel of VOXELS type: " + xml.name().toString());
989 }
990 m_voxelIndicesIJK = readIndexArray(xml);
991 xml.readNext();//remove the end element of VoxelIndicesIJK
992 if (xml.hasError()) return;
993 #else
994 #ifdef CIFTILIB_USE_XMLPP
995 bool haveVoxelIndices = false, done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
996 while(!done && xml.read())
997 {
998 switch (xml.get_node_type())
999 {
1000 case XmlReader::Element:
1001 {
1002 AString name = xml.get_local_name();
1003 if (name == "VoxelIndicesIJK")
1004 {
1005 if (haveVoxelIndices)
1006 {
1007 throw CiftiException("VoxelIndicesIJK element may only be specified once");
1008 }
1009 m_voxelIndicesIJK = readIndexArray(xml);
1010 haveVoxelIndices = true;
1011 } else {
1012 throw CiftiException("unexpected element in BrainModel: " + name);
1013 }
1014 break;
1015 }
1016 case XmlReader::EndElement:
1017 done = true;
1018 break;
1019 default:
1020 break;
1021 }
1022 }
1023 if (!haveVoxelIndices)//special case in cifti-1
1024 {
1025 throw CiftiException("BrainModel requires a child element");
1026 }
1027 #else
1028 #error "not implemented"
1029 #endif
1030 #endif
1031 if (m_voxelIndicesIJK.size() % 3 != 0)
1032 {
1033 throw CiftiException("number of voxel indices is not a multiple of 3");
1034 }
1035 if ((int64_t)m_voxelIndicesIJK.size() != m_count * 3)
1036 {
1037 throw CiftiException("number of voxel indices does not match IndexCount");
1038 }
1039 }
1040 #ifdef CIFTILIB_USE_QT
1041 while (!xml.atEnd() && !xml.isEndElement())//locate the end element of BrainModel
1042 {
1043 switch(xml.readNext())
1044 {
1045 case QXmlStreamReader::StartElement:
1046 throw CiftiException("unexpected second element in BrainModel: " + xml.name().toString());
1047 default:
1048 break;
1049 }
1050 }
1051 #endif
1052 CiftiAssert(XmlReader_checkEndElement(xml, "BrainModel"));
1053 }
1054
1055 vector<int64_t> CiftiBrainModelsMap::ParseHelperModel::readIndexArray(XmlReader& xml)
1056 {
1057 vector<int64_t> ret;
1058 AString text = XmlReader_readElementText(xml);//throws if it encounters a start element
1059 #ifdef CIFTILIB_USE_QT
1060 if (xml.hasError()) return ret;
1061 #endif
1062 vector<AString> separated = AString_split_whitespace(text);
1063 size_t numElems = separated.size();
1064 ret.reserve(numElems);
1065 for (size_t i = 0; i < numElems; ++i)
1066 {
1067 bool ok = false;
1068 ret.push_back(AString_toInt(separated[i], ok));
1069 if (!ok)
1070 {
1071 throw CiftiException("found noninteger in index array: " + separated[i]);
1072 }
1073 if (ret.back() < 0)
1074 {
1075 throw CiftiException("found negative integer in index array: " + separated[i]);
1076 }
1077 }
1078 return ret;
1079 }
1080
1081 void CiftiBrainModelsMap::writeXML1(XmlWriter& xml) const
1082 {
1083 CiftiAssert(!m_ignoreVolSpace);
1084 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_BRAIN_MODELS");
1085 int numModels = (int)m_modelsInfo.size();
1086 for (int i = 0; i < numModels; ++i)
1087 {
1088 const BrainModelPriv& myModel = m_modelsInfo[i];
1089 xml.writeStartElement("BrainModel");
1090 xml.writeAttribute("IndexOffset", AString_number(myModel.m_modelStart));
1091 xml.writeAttribute("IndexCount", AString_number(myModel.m_modelEnd - myModel.m_modelStart));
1092 xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(myModel.m_brainStructure));
1093 if (myModel.m_type == SURFACE)
1094 {
1095 xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_SURFACE");
1096 xml.writeAttribute("SurfaceNumberOfNodes", AString_number(myModel.m_surfaceNumberOfNodes));
1097 xml.writeStartElement("NodeIndices");
1098 AString text = "";
1099 int64_t numNodes = (int64_t)myModel.m_nodeIndices.size();
1100 for (int64_t j = 0; j < numNodes; ++j)
1101 {
1102 if (j != 0) text += " ";
1103 text += AString_number(myModel.m_nodeIndices[j]);
1104 }
1105 xml.writeCharacters(text);
1106 xml.writeEndElement();
1107 } else {
1108 xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_VOXELS");
1109 xml.writeStartElement("VoxelIndicesIJK");
1110 AString text = "";
1111 int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size();
1112 CiftiAssert(listSize % 3 == 0);
1113 for (int64_t j = 0; j < listSize; j += 3)
1114 {
1115 text += AString_number(myModel.m_voxelIndicesIJK[j]) + " " + AString_number(myModel.m_voxelIndicesIJK[j + 1]) + " " + AString_number(myModel.m_voxelIndicesIJK[j + 2]) + "\n";
1116 }
1117 xml.writeCharacters(text);
1118 xml.writeEndElement();
1119 }
1120 xml.writeEndElement();
1121 }
1122 }
1123
1124 void CiftiBrainModelsMap::writeXML2(XmlWriter& xml) const
1125 {
1126 CiftiAssert(!m_ignoreVolSpace);
1127 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_BRAIN_MODELS");
1128 if (hasVolumeData())//could be m_haveVolumeSpace if we want to be able to write a volspace without having voxels, but that seems silly
1129 {
1130 m_volSpace.writeCiftiXML2(xml);
1131 }
1132 int numModels = (int)m_modelsInfo.size();
1133 for (int i = 0; i < numModels; ++i)
1134 {
1135 const BrainModelPriv& myModel = m_modelsInfo[i];
1136 xml.writeStartElement("BrainModel");
1137 xml.writeAttribute("IndexOffset", AString_number(myModel.m_modelStart));
1138 xml.writeAttribute("IndexCount", AString_number(myModel.m_modelEnd - myModel.m_modelStart));
1139 xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(myModel.m_brainStructure));
1140 if (myModel.m_type == SURFACE)
1141 {
1142 xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_SURFACE");
1143 xml.writeAttribute("SurfaceNumberOfVertices", AString_number(myModel.m_surfaceNumberOfNodes));
1144 xml.writeStartElement("VertexIndices");
1145 AString text = "";
1146 int64_t numNodes = (int64_t)myModel.m_nodeIndices.size();
1147 for (int64_t j = 0; j < numNodes; ++j)
1148 {
1149 if (j != 0) text += " ";
1150 text += AString_number(myModel.m_nodeIndices[j]);
1151 }
1152 xml.writeCharacters(text);
1153 xml.writeEndElement();
1154 } else {
1155 xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_VOXELS");
1156 xml.writeStartElement("VoxelIndicesIJK");
1157 AString text = "";
1158 int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size();
1159 CiftiAssert(listSize % 3 == 0);
1160 for (int64_t j = 0; j < listSize; j += 3)
1161 {
1162 text += AString_number(myModel.m_voxelIndicesIJK[j]) + " " + AString_number(myModel.m_voxelIndicesIJK[j + 1]) + " " + AString_number(myModel.m_voxelIndicesIJK[j + 2]) + "\n";
1163 }
1164 xml.writeCharacters(text);
1165 xml.writeEndElement();
1166 }
1167 xml.writeEndElement();
1168 }
1169 }
0 #ifndef __CIFTI_BRAIN_MODELS_MAP_H__
1 #define __CIFTI_BRAIN_MODELS_MAP_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiMappingType.h"
31
32 #include "Common/Compact3DLookup.h"
33 #include "StructureEnum.h"
34 #include "VolumeSpace.h"
35
36 #include <map>
37 #include <utility>
38 #include <vector>
39
40 namespace cifti
41 {
42 class CiftiBrainModelsMap : public CiftiMappingType
43 {
44 public:
45 enum ModelType
46 {
47 SURFACE,
48 VOXELS
49 };
50 struct SurfaceMap
51 {
52 int64_t m_ciftiIndex;
53 int64_t m_surfaceNode;
54 };
55 struct VolumeMap
56 {
57 int64_t m_ciftiIndex;
58 int64_t m_ijk[3];
59 };
60 struct ModelInfo
61 {
62 ModelType m_type;
63 StructureEnum::Enum m_structure;
64 int64_t m_indexStart, m_indexCount;//these are intended only for summary info, use getSurfaceMap, etc for the index to vertex/voxel mappings
65 };
66 struct IndexInfo
67 {
68 ModelType m_type;
69 StructureEnum::Enum m_structure;
70 int64_t m_surfaceNode;//only one of these two will be valid
71 int64_t m_ijk[3];
72 };
73 bool hasVolumeData() const;
74 bool hasVolumeData(const StructureEnum::Enum& structure) const;
75 bool hasSurfaceData(const StructureEnum::Enum& structure) const;
76 int64_t getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const;
77 int64_t getIndexForVoxel(const int64_t* ijk, StructureEnum::Enum* structureOut = NULL) const;
78 int64_t getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k, StructureEnum::Enum* structureOut = NULL) const;
79 IndexInfo getInfoForIndex(const int64_t index) const;
80 std::vector<SurfaceMap> getSurfaceMap(const StructureEnum::Enum& structure) const;
81 std::vector<VolumeMap> getFullVolumeMap() const;
82 std::vector<VolumeMap> getVolumeStructureMap(const StructureEnum::Enum& structure) const;
83 const VolumeSpace& getVolumeSpace() const;
84 int64_t getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const;
85 std::vector<StructureEnum::Enum> getSurfaceStructureList() const;
86 std::vector<StructureEnum::Enum> getVolumeStructureList() const;
87 const std::vector<int64_t>& getNodeList(const StructureEnum::Enum& structure) const;//useful for copying mappings to a new dense mapping
88 const std::vector<int64_t>& getVoxelList(const StructureEnum::Enum& structure) const;
89 std::vector<ModelInfo> getModelInfo() const;
90
91 CiftiBrainModelsMap() { m_haveVolumeSpace = false; m_ignoreVolSpace = false; }
92 void addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const float* roi = NULL);
93 void addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const std::vector<int64_t>& nodeList);
94 void addVolumeModel(const StructureEnum::Enum& structure, const std::vector<int64_t>& ijkList);
95 void setVolumeSpace(const VolumeSpace& space);
96 void clear();
97
98 CiftiMappingType* clone() const { return new CiftiBrainModelsMap(*this); }
99 MappingType getType() const { return BRAIN_MODELS; }
100 int64_t getLength() const;
101 bool operator==(const CiftiMappingType& rhs) const;
102 bool approximateMatch(const CiftiMappingType& rhs, AString* explanation = NULL) const;
103 void readXML1(XmlReader& xml);
104 void readXML2(XmlReader& xml);
105 void writeXML1(XmlWriter& xml) const;
106 void writeXML2(XmlWriter& xml) const;
107 private:
108 struct BrainModelPriv
109 {
110 ModelType m_type;
111 StructureEnum::Enum m_brainStructure;
112 int64_t m_surfaceNumberOfNodes;
113 std::vector<int64_t> m_nodeIndices;
114 std::vector<int64_t> m_voxelIndicesIJK;
115
116 int64_t m_modelStart, m_modelEnd;//stuff only needed for optimization - models are kept in sorted order by their index ranges
117 std::vector<int64_t> m_nodeToIndexLookup;
118 bool operator==(const BrainModelPriv& rhs) const;
119 bool operator!=(const BrainModelPriv& rhs) const { return !((*this) == rhs); }
120 void setupSurface(const int64_t& start);
121 };
122 VolumeSpace m_volSpace;
123 bool m_haveVolumeSpace, m_ignoreVolSpace;//second is needed for parsing cifti-1
124 std::vector<BrainModelPriv> m_modelsInfo;
125 std::map<StructureEnum::Enum, int> m_surfUsed, m_volUsed;
126 Compact3DLookup<std::pair<int64_t, StructureEnum::Enum> > m_voxelToIndexLookup;//make one unified lookup rather than separate lookups per volume structure
127 int64_t getNextStart() const;
128 struct ParseHelperModel
129 {//specifically to allow the parsed elements to be sorted before using addSurfaceModel/addVolumeModel
130 ModelType m_type;
131 StructureEnum::Enum m_brainStructure;
132 int64_t m_surfaceNumberOfNodes;
133 std::vector<int64_t> m_nodeIndices;
134 std::vector<int64_t> m_voxelIndicesIJK;
135 int64_t m_offset, m_count;
136 bool operator<(const ParseHelperModel& rhs) const
137 {
138 if (m_offset < rhs.m_offset) return true;
139 if (m_offset > rhs.m_offset) return false;//get the common cases first
140 if (m_count < rhs.m_count) return true;//in case we have a zero-length model - this shouldn't happen, usually
141 return false;
142 }
143 void parseBrainModel1(XmlReader& xml);
144 void parseBrainModel2(XmlReader& xml);
145 static std::vector<int64_t> readIndexArray(XmlReader& xml);
146 };
147 };
148 }
149
150 #endif //__CIFTI_BRAIN_MODELS_MAP_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiLabelsMap.h"
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/CiftiException.h"
31
32 #include <iostream>
33
34 using namespace std;
35 using namespace cifti;
36
37 void CiftiLabelsMap::clear()
38 {
39 m_maps.clear();
40 }
41
42 const MetaData& CiftiLabelsMap::getMapMetadata(const int64_t& index) const
43 {
44 CiftiAssertVectorIndex(m_maps, index);
45 return m_maps[index].m_metaData;
46 }
47
48 const LabelTable& CiftiLabelsMap::getMapLabelTable(const int64_t& index) const
49 {
50 CiftiAssertVectorIndex(m_maps, index);
51 return m_maps[index].m_labelTable;
52 }
53
54 const AString& CiftiLabelsMap::getMapName(const int64_t& index) const
55 {
56 CiftiAssertVectorIndex(m_maps, index);
57 return m_maps[index].m_name;
58 }
59
60 void CiftiLabelsMap::setMapMetadata(const int64_t& index, const MetaData& md)
61 {
62 CiftiAssertVectorIndex(m_maps, index);
63 m_maps[index].m_metaData = md;
64 }
65
66 void CiftiLabelsMap::setMapLabelTable(const int64_t& index, const LabelTable& lt)
67 {
68 CiftiAssertVectorIndex(m_maps, index);
69 m_maps[index].m_labelTable = lt;
70 }
71
72 void CiftiLabelsMap::setMapName(const int64_t& index, const AString& mapName)
73 {
74 CiftiAssertVectorIndex(m_maps, index);
75 m_maps[index].m_name = mapName;
76 }
77
78 void CiftiLabelsMap::setLength(const int64_t& length)
79 {
80 CiftiAssert(length > 0);
81 m_maps.resize(length);
82 }
83
84 bool CiftiLabelsMap::approximateMatch(const CiftiMappingType& rhs, AString* explanation) const
85 {
86 switch (rhs.getType())
87 {
88 case SCALARS:
89 case SERIES://maybe?
90 case LABELS:
91 if (getLength() != rhs.getLength())
92 {
93 if (explanation != NULL) *explanation = "mappings have different length";
94 return false;
95 } else return true;
96 default:
97 if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType());
98 return false;
99 }
100 }
101
102 bool CiftiLabelsMap::operator==(const CiftiMappingType& rhs) const
103 {
104 if (rhs.getType() != getType()) return false;
105 const CiftiLabelsMap& myrhs = dynamic_cast<const CiftiLabelsMap&>(rhs);
106 return (m_maps == myrhs.m_maps);
107 }
108
109 bool CiftiLabelsMap::LabelMap::operator==(const LabelMap& rhs) const
110 {
111 if (m_name != rhs.m_name) return false;
112 if (m_labelTable != rhs.m_labelTable) return false;
113 return (m_metaData == rhs.m_metaData);
114 }
115
116 void CiftiLabelsMap::readXML1(XmlReader& xml)
117 {
118 cerr << "parsing nonstandard labels mapping type in cifti-1" << endl;
119 clear();
120 #ifdef CIFTILIB_USE_QT
121 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
122 {
123 switch (xml.tokenType())
124 {
125 case QXmlStreamReader::StartElement:
126 {
127 if (xml.name() != "NamedMap")
128 {
129 throw CiftiException("unexpected element in labels map: " + xml.name().toString());
130 }
131 LabelMap tempMap;
132 tempMap.readXML1(xml);
133 if (xml.hasError()) return;
134 m_maps.push_back(tempMap);
135 break;
136 }
137 default:
138 break;
139 }
140 }
141 #else
142 #ifdef CIFTILIB_USE_XMLPP
143 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
144 while(!done && xml.read())
145 {
146 switch (xml.get_node_type())
147 {
148 case XmlReader::Element:
149 {
150 AString name = xml.get_local_name();
151 if (name == "NamedMap")
152 {
153 LabelMap tempMap;
154 tempMap.readXML1(xml);
155 m_maps.push_back(tempMap);
156 } else {
157 throw CiftiException("unexpected element in labels map: " + name);
158 }
159 break;
160 }
161 case XmlReader::EndElement:
162 done = true;
163 break;
164 default:
165 break;
166 }
167 }
168 #else
169 #error "not implemented"
170 #endif
171 #endif
172 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
173 }
174
175 void CiftiLabelsMap::readXML2(XmlReader& xml)
176 {
177 clear();
178 #ifdef CIFTILIB_USE_QT
179 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
180 {
181 switch (xml.tokenType())
182 {
183 case QXmlStreamReader::StartElement:
184 {
185 if (xml.name() != "NamedMap")
186 {
187 throw CiftiException("unexpected element in labels mapping type: " + xml.name().toString());
188 }
189 LabelMap tempMap;
190 tempMap.readXML2(xml);
191 if (xml.hasError()) return;
192 m_maps.push_back(tempMap);
193 break;
194 }
195 default:
196 break;
197 }
198 }
199 #else
200 #ifdef CIFTILIB_USE_XMLPP
201 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
202 while(!done && xml.read())
203 {
204 switch (xml.get_node_type())
205 {
206 case XmlReader::Element:
207 {
208 AString name = xml.get_local_name();
209 if (name == "NamedMap")
210 {
211 LabelMap tempMap;
212 tempMap.readXML2(xml);
213 m_maps.push_back(tempMap);
214 } else {
215 throw CiftiException("unexpected element in labels mapping type: " + name);
216 }
217 break;
218 }
219 case XmlReader::EndElement:
220 done = true;
221 break;
222 default:
223 break;
224 }
225 }
226 #else
227 #error "not implemented"
228 #endif
229 #endif
230 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
231 }
232
233 void CiftiLabelsMap::LabelMap::readXML1(XmlReader& xml)
234 {
235 bool haveName = false, haveTable = false, haveMetaData = false;
236 #ifdef CIFTILIB_USE_QT
237 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
238 {
239 switch (xml.tokenType())
240 {
241 case QXmlStreamReader::StartElement:
242 {
243 QStringRef name = xml.name();
244 if (name == "MetaData")
245 {
246 if (haveMetaData)
247 {
248 throw CiftiException("MetaData specified multiple times in one NamedMap");
249 }
250 m_metaData.readCiftiXML1(xml);
251 if (xml.hasError()) return;
252 haveMetaData = true;
253 } else if (name == "LabelTable") {
254 if (haveTable)
255 {
256 throw CiftiException("LabelTable specified multiple times in one NamedMap");
257 }
258 m_labelTable.readXml(xml);
259 if (xml.hasError()) return;
260 haveTable = true;
261 } else if (name == "MapName") {
262 if (haveName)
263 {
264 throw CiftiException("MapName specified multiple times in one NamedMap");
265 }
266 m_name = xml.readElementText();//raises error if element encountered
267 if (xml.hasError()) return;
268 haveName = true;
269 } else {
270 throw CiftiException("unexpected element in NamedMap: " + name.toString());
271 }
272 break;
273 }
274 default:
275 break;
276 }
277 }
278 #else
279 #ifdef CIFTILIB_USE_XMLPP
280 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
281 while(!done && xml.read())
282 {
283 switch (xml.get_node_type())
284 {
285 case XmlReader::Element:
286 {
287 AString name = xml.get_local_name();
288 if (name == "MetaData")
289 {
290 if (haveMetaData)
291 {
292 throw CiftiException("MetaData specified multiple times in one NamedMap");
293 }
294 m_metaData.readCiftiXML1(xml);
295 haveMetaData = true;
296 } else if (name == "LabelTable") {
297 if (haveTable)
298 {
299 throw CiftiException("LabelTable specified multiple times in one NamedMap");
300 }
301 m_labelTable.readXml(xml);
302 haveTable = true;
303 } else if (name == "MapName") {
304 if (haveName)
305 {
306 throw CiftiException("MapName specified multiple times in one NamedMap");
307 }
308 m_name = XmlReader_readElementText(xml);//raises error if element encountered
309 haveName = true;
310 } else {
311 throw CiftiException("unexpected element in labels mapping type: " + name);
312 }
313 break;
314 }
315 case XmlReader::EndElement:
316 done = true;
317 break;
318 default:
319 break;
320 }
321 }
322 #else
323 #error "not implemented"
324 #endif
325 #endif
326 CiftiAssert(XmlReader_checkEndElement(xml, "NamedMap"));
327 if (!haveName)
328 {
329 throw CiftiException("NamedMap missing required child element MapName");
330 }
331 if (!haveTable)
332 {
333 throw CiftiException("NamedMap in labels mapping missing required child element LabelTable");
334 }
335 }
336
337 void CiftiLabelsMap::LabelMap::readXML2(XmlReader& xml)
338 {
339 bool haveName = false, haveTable = false, haveMetaData = false;
340 #ifdef CIFTILIB_USE_QT
341 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
342 {
343 switch (xml.tokenType())
344 {
345 case QXmlStreamReader::StartElement:
346 {
347 QStringRef name = xml.name();
348 if (name == "MetaData")
349 {
350 if (haveMetaData)
351 {
352 throw CiftiException("MetaData specified multiple times in one NamedMap");
353 }
354 m_metaData.readCiftiXML2(xml);
355 if (xml.hasError()) return;
356 haveMetaData = true;
357 } else if (name == "LabelTable") {
358 if (haveTable)
359 {
360 throw CiftiException("LabelTable specified multiple times in one NamedMap");
361 }
362 m_labelTable.readXml(xml);
363 if (xml.hasError()) return;
364 haveTable = true;
365 } else if (name == "MapName") {
366 if (haveName)
367 {
368 throw CiftiException("MapName specified multiple times in one NamedMap");
369 }
370 m_name = xml.readElementText();//raises error if element encountered
371 if (xml.hasError()) return;
372 haveName = true;
373 } else {
374 throw CiftiException("unexpected element in NamedMap: " + name.toString());
375 }
376 break;
377 }
378 default:
379 break;
380 }
381 }
382 #else
383 #ifdef CIFTILIB_USE_XMLPP
384 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
385 while(!done && xml.read())
386 {
387 switch (xml.get_node_type())
388 {
389 case XmlReader::Element:
390 {
391 AString name = xml.get_local_name();
392 if (name == "MetaData")
393 {
394 if (haveMetaData)
395 {
396 throw CiftiException("MetaData specified multiple times in one NamedMap");
397 }
398 m_metaData.readCiftiXML2(xml);
399 haveMetaData = true;
400 } else if (name == "LabelTable") {
401 if (haveTable)
402 {
403 throw CiftiException("LabelTable specified multiple times in one NamedMap");
404 }
405 m_labelTable.readXml(xml);
406 haveTable = true;
407 } else if (name == "MapName") {
408 if (haveName)
409 {
410 throw CiftiException("MapName specified multiple times in one NamedMap");
411 }
412 m_name = XmlReader_readElementText(xml);//raises error if element encountered
413 haveName = true;
414 } else {
415 throw CiftiException("unexpected element in labels mapping type: " + name);
416 }
417 break;
418 }
419 case XmlReader::EndElement:
420 done = true;
421 break;
422 default:
423 break;
424 }
425 }
426 #else
427 #error "not implemented"
428 #endif
429 #endif
430 CiftiAssert(XmlReader_checkEndElement(xml, "NamedMap"));
431 if (!haveName)
432 {
433 throw CiftiException("NamedMap missing required child element MapName");
434 }
435 if (!haveTable)
436 {
437 throw CiftiException("NamedMap in labels mapping missing required child element LabelTable");
438 }
439 }
440
441 void CiftiLabelsMap::writeXML1(XmlWriter& xml) const
442 {
443 cerr << "writing nonstandard labels mapping type in cifti-1" << endl;
444 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_LABELS");
445 int64_t numMaps = (int64_t)m_maps.size();
446 for (int64_t i = 0; i < numMaps; ++i)
447 {
448 xml.writeStartElement("NamedMap");
449 xml.writeTextElement("MapName", m_maps[i].m_name);
450 m_maps[i].m_metaData.writeCiftiXML1(xml);
451 m_maps[i].m_labelTable.writeXML(xml);
452 xml.writeEndElement();
453 }
454 }
455
456 void CiftiLabelsMap::writeXML2(XmlWriter& xml) const
457 {
458 int64_t numMaps = (int64_t)m_maps.size();
459 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_LABELS");
460 for (int64_t i = 0; i < numMaps; ++i)
461 {
462 xml.writeStartElement("NamedMap");
463 xml.writeTextElement("MapName", m_maps[i].m_name);
464 m_maps[i].m_metaData.writeCiftiXML2(xml);
465 m_maps[i].m_labelTable.writeXML(xml);
466 xml.writeEndElement();
467 }
468 }
0 #ifndef __CIFTI_LABELS_MAP_H__
1 #define __CIFTI_LABELS_MAP_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiMappingType.h"
31
32 #include "Common/AString.h"
33 #include "MetaData.h"
34 #include "LabelTable.h"
35
36 #include <map>
37 #include <vector>
38
39 namespace cifti
40 {
41 class CiftiLabelsMap : public CiftiMappingType
42 {
43 public:
44 const MetaData& getMapMetadata(const int64_t& index) const;
45 const LabelTable& getMapLabelTable(const int64_t& index) const;
46 const AString& getMapName(const int64_t& index) const;
47
48 void setMapMetadata(const int64_t& index, const MetaData& md);
49 void setMapLabelTable(const int64_t& index, const LabelTable& lt);
50 void setMapName(const int64_t& index, const AString& mapName);
51 void setLength(const int64_t& length);
52 void clear();
53
54 CiftiMappingType* clone() const { return new CiftiLabelsMap(*this); }
55 MappingType getType() const { return LABELS; }
56 int64_t getLength() const { return m_maps.size(); }
57 bool operator==(const CiftiMappingType& rhs) const;
58 bool approximateMatch(const CiftiMappingType& rhs, AString* explanation = NULL) const;
59 void readXML1(XmlReader& xml);
60 void readXML2(XmlReader& xml);
61 void writeXML1(XmlWriter& xml) const;
62 void writeXML2(XmlWriter& xml) const;
63 private:
64 struct LabelMap
65 {
66 AString m_name;
67 MetaData m_metaData;
68 LabelTable m_labelTable;
69 bool operator==(const LabelMap& rhs) const;
70 void readXML1(XmlReader& xml);
71 void readXML2(XmlReader& xml);
72 };
73 std::vector<LabelMap> m_maps;
74 };
75 }
76
77 #endif //__CIFTI_LABELS_MAP_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiMappingType.h"
28
29 #include "Common/CiftiAssert.h"
30
31 using namespace cifti;
32
33 CiftiMappingType::~CiftiMappingType()
34 {//to ensure that the class's vtable gets defined in an object file
35 }
36
37 AString CiftiMappingType::mappingTypeToName(const CiftiMappingType::MappingType& type)
38 {
39 switch (type)
40 {
41 case BRAIN_MODELS:
42 return "BRAIN_MODELS";
43 case PARCELS:
44 return "PARCELS";
45 case SERIES:
46 return "SERIES";
47 case SCALARS:
48 return "SCALARS";
49 case LABELS:
50 return "LABELS";
51 }
52 CiftiAssert(0);
53 return "";
54 }
0 #ifndef __CIFTI_MAPPING_TYPE_H__
1 #define __CIFTI_MAPPING_TYPE_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "stdint.h"
31
32 #include "Common/XmlAdapter.h"
33
34 namespace cifti
35 {
36
37 class CiftiMappingType
38 {
39 public:
40 enum MappingType
41 {
42 BRAIN_MODELS,
43 PARCELS,
44 SERIES,
45 SCALARS,
46 LABELS
47 };
48 virtual CiftiMappingType* clone() const = 0;//make a copy, preserving the actual type - NOTE: this returns a dynamic allocation that is not owned by anything
49 virtual MappingType getType() const = 0;
50 virtual int64_t getLength() const = 0;
51 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
52 bool operator!=(const CiftiMappingType& rhs) const { return !((*this) == rhs); }
53 virtual bool approximateMatch(const CiftiMappingType& rhs, AString* explanation = NULL) const = 0;//check if things like doing index-wise math would make sense
54 virtual void readXML1(XmlReader& xml) = 0;//mainly to shorten the type-specific code in CiftiXML
55 virtual void readXML2(XmlReader& xml) = 0;
56 virtual void writeXML1(XmlWriter& xml) const = 0;
57 virtual void writeXML2(XmlWriter& xml) const = 0;
58 virtual ~CiftiMappingType();
59
60 static AString mappingTypeToName(const MappingType& type);
61 };
62 }
63
64 #endif //__CIFTI_MAPPING_TYPE_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiParcelsMap.h"
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/CiftiException.h"
31
32 #include <iostream>
33
34 using namespace std;
35 using namespace cifti;
36
37 void CiftiParcelsMap::addParcel(const CiftiParcelsMap::Parcel& parcel)
38 {
39 int64_t thisParcel = m_parcels.size();//slight hack: current number of parcels will be this parcel's index
40 for (int64_t i = 0; i < thisParcel; ++i)//also use it as endpoint of looping over existing parcels
41 {
42 if (parcel.m_name == m_parcels[i].m_name)
43 {
44 throw CiftiException("cannot add parcel with duplicate name '" + parcel.m_name + "'");//NOTE: technically this restriction isn't in the standard, but that was probably an oversight
45 }
46 }
47 int64_t voxelListSize = (int64_t)parcel.m_voxelIndices.size();
48 Compact3DLookup<int64_t> tempLookup = m_volLookup;//a copy of the lookup should be faster than other methods of checking for overlap and repeat
49 if (voxelListSize != 0)
50 {
51 const int64_t* dims = NULL;
52 if (!m_ignoreVolSpace)
53 {
54 if (!m_haveVolumeSpace)
55 {
56 throw CiftiException("you must set the volume space before adding parcels that use voxels");
57 }
58 dims = m_volSpace.getDims();
59 }
60 for (set<VoxelIJK>::const_iterator iter = parcel.m_voxelIndices.begin(); iter != parcel.m_voxelIndices.end(); ++iter)//do all error checking before adding to lookup - might be unnecessary
61 {
62 if (iter->m_ijk[0] < 0 || iter->m_ijk[1] < 0 || iter->m_ijk[2] < 0)
63 {
64 throw CiftiException("found negative index triple in voxel list");
65 }
66 if (!m_ignoreVolSpace && (iter->m_ijk[0] >= dims[0] ||
67 iter->m_ijk[1] >= dims[1] ||
68 iter->m_ijk[2] >= dims[2]))
69 {
70 throw CiftiException("found invalid index triple in voxel list");
71 }
72 if (tempLookup.find(iter->m_ijk) != NULL)
73 {
74 throw CiftiException("parcels may not overlap in voxels");
75 }
76 tempLookup.at(iter->m_ijk) = thisParcel;
77 }
78 }
79 for (map<StructureEnum::Enum, set<int64_t> >::const_iterator iter = parcel.m_surfaceNodes.begin(); iter != parcel.m_surfaceNodes.end(); ++iter)
80 {
81 map<StructureEnum::Enum, SurfaceInfo>::const_iterator info = m_surfInfo.find(iter->first);
82 if (info == m_surfInfo.end())
83 {
84 throw CiftiException("you must set surfaces before adding parcels that use them");
85 }
86 const set<int64_t>& nodeSet = iter->second;
87 if (nodeSet.size() == 0)
88 {
89 throw CiftiException("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
90 }
91 for (set<int64_t>::const_iterator iter2 = nodeSet.begin(); iter2 != nodeSet.end(); ++iter2)
92 {
93 if (*iter2 < 0)
94 {
95 throw CiftiException("found negative vertex in parcel");
96 }
97 if (*iter2 >= info->second.m_numNodes)
98 {
99 throw CiftiException("found invalid vertex in parcel");
100 }
101 if (info->second.m_lookup[*iter2] != -1)
102 {
103 throw CiftiException("parcels may not overlap in vertices");
104 }
105 }
106 }
107 if (voxelListSize != 0)//all error checking done, modify
108 {
109 m_volLookup = tempLookup;
110 }
111 for (map<StructureEnum::Enum, set<int64_t> >::const_iterator iter = parcel.m_surfaceNodes.begin(); iter != parcel.m_surfaceNodes.end(); ++iter)
112 {
113 map<StructureEnum::Enum, SurfaceInfo>::iterator info = m_surfInfo.find(iter->first);
114 CiftiAssert(info != m_surfInfo.end());
115 const set<int64_t>& nodeSet = iter->second;
116 for (set<int64_t>::const_iterator iter2 = nodeSet.begin(); iter2 != nodeSet.end(); ++iter2)
117 {
118 CiftiAssertVectorIndex(info->second.m_lookup, *iter2);
119 info->second.m_lookup[*iter2] = thisParcel;
120 }
121 }
122 m_parcels.push_back(parcel);
123 }
124
125 void CiftiParcelsMap::addSurface(const int64_t& numberOfNodes, const StructureEnum::Enum& structure)
126 {
127 map<StructureEnum::Enum, SurfaceInfo>::const_iterator test = m_surfInfo.find(structure);
128 if (test != m_surfInfo.end())
129 {
130 throw CiftiException("parcel surface structures may not be used more than once");
131 }
132 SurfaceInfo tempInfo;
133 tempInfo.m_numNodes = numberOfNodes;
134 tempInfo.m_lookup.resize(numberOfNodes, -1);
135 m_surfInfo[structure] = tempInfo;
136 }
137
138 void CiftiParcelsMap::clear()
139 {
140 m_haveVolumeSpace = false;
141 m_ignoreVolSpace = false;
142 m_parcels.clear();
143 m_surfInfo.clear();
144 m_volLookup.clear();
145 }
146
147 void CiftiParcelsMap::setVolumeSpace(const VolumeSpace& space)
148 {
149 const int64_t* dims = space.getDims();
150 int64_t numParcels = (int64_t)m_parcels.size();
151 for (int64_t i = 0; i < numParcels; ++i)
152 {
153 const set<VoxelIJK>& voxelList = m_parcels[i].m_voxelIndices;
154 for (set<VoxelIJK>::const_iterator iter = voxelList.begin(); iter != voxelList.end(); ++iter)
155 {
156 if (iter->m_ijk[0] >= dims[0] ||
157 iter->m_ijk[1] >= dims[1] ||
158 iter->m_ijk[2] >= dims[2])
159 {
160 throw CiftiException("parcels may not contain voxel indices outside the volume space");
161 }
162 }
163 }
164 m_haveVolumeSpace = true;
165 m_ignoreVolSpace = false;
166 m_volSpace = space;
167 }
168
169 int64_t CiftiParcelsMap::getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const
170 {
171 CiftiAssert(node >= 0);
172 map<StructureEnum::Enum, SurfaceInfo>::const_iterator test = m_surfInfo.find(structure);
173 if (test == m_surfInfo.end()) return -1;
174 if (node >= test->second.m_numNodes) return -1;
175 CiftiAssertVectorIndex(test->second.m_lookup, node);
176 return test->second.m_lookup[node];
177 }
178
179 int64_t CiftiParcelsMap::getIndexForVoxel(const int64_t* ijk) const
180 {
181 return getIndexForVoxel(ijk[0], ijk[1], ijk[2]);
182 }
183
184 int64_t CiftiParcelsMap::getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k) const
185 {
186 const int64_t* test = m_volLookup.find(i, j, k);//the lookup tolerates weirdness like negatives
187 if (test == NULL) return -1;
188 return *test;
189 }
190
191 vector<StructureEnum::Enum> CiftiParcelsMap::getParcelSurfaceStructures() const
192 {
193 vector<StructureEnum::Enum> ret;
194 ret.reserve(m_surfInfo.size());
195 for (map<StructureEnum::Enum, SurfaceInfo>::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter)
196 {
197 ret.push_back(iter->first);
198 }
199 return ret;
200 }
201
202 const VolumeSpace& CiftiParcelsMap::getVolumeSpace() const
203 {
204 CiftiAssert(!m_ignoreVolSpace);//this should never be set except during parsing of cifti-1
205 if (!m_haveVolumeSpace)
206 {
207 throw CiftiException("getVolumeSpace called when no volume space exists");
208 }
209 return m_volSpace;
210 }
211
212 int64_t CiftiParcelsMap::getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const
213 {
214 map<StructureEnum::Enum, SurfaceInfo>::const_iterator iter = m_surfInfo.find(structure);
215 if (iter == m_surfInfo.end()) return -1;
216 return iter->second.m_numNodes;
217 }
218
219 bool CiftiParcelsMap::hasSurface(const StructureEnum::Enum& structure) const
220 {
221 return m_surfInfo.find(structure) != m_surfInfo.end();
222 }
223
224 bool CiftiParcelsMap::hasSurfaceData(const StructureEnum::Enum& structure) const
225 {
226 if (m_surfInfo.find(structure) == m_surfInfo.end()) return false;
227 int64_t numParcels = (int64_t)m_parcels.size();
228 for (int64_t i = 0; i < numParcels; ++i)
229 {
230 map<StructureEnum::Enum, set<int64_t> >::const_iterator iter = m_parcels[i].m_surfaceNodes.find(structure);
231 if (iter != m_parcels[i].m_surfaceNodes.end() && iter->second.size() != 0) return true;
232 }
233 return false;
234 }
235
236 bool CiftiParcelsMap::hasVolumeData() const
237 {
238 CiftiAssert(!m_ignoreVolSpace);
239 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
240 for (int64_t i = 0; i < numParcels; ++i)
241 {
242 if (m_parcels[i].m_voxelIndices.size() != 0) return true;
243 }
244 return false;
245 }
246
247 bool CiftiParcelsMap::approximateMatch(const CiftiMappingType& rhs, AString* explanation) const
248 {
249 if (rhs.getType() != getType())
250 {
251 if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType());
252 return false;
253 }
254 const CiftiParcelsMap& myrhs = dynamic_cast<const CiftiParcelsMap&>(rhs);
255 CiftiAssert(!m_ignoreVolSpace && !myrhs.m_ignoreVolSpace);
256 if (m_haveVolumeSpace != myrhs.m_haveVolumeSpace)
257 {
258 if (explanation != NULL) *explanation = "one of the mappings has no volume data";
259 return false;
260 }
261 if (m_haveVolumeSpace && (m_volSpace != myrhs.m_volSpace))
262 {
263 if (explanation != NULL) *explanation = "mappings have a different volume space";
264 return false;
265 }
266 if (m_surfInfo.size() != myrhs.m_surfInfo.size())
267 {
268 if (explanation != NULL) *explanation = "mappings have a different number of surfaces used";
269 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
270 }
271 for (map<StructureEnum::Enum, SurfaceInfo>::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter)
272 {
273 map<StructureEnum::Enum, SurfaceInfo>::const_iterator rhsiter = myrhs.m_surfInfo.find(iter->first);
274 if (rhsiter == myrhs.m_surfInfo.end())
275 {//technically, they might still have the same meaning, if the surface isn't used, but they will still write differently, so false
276 if (explanation != NULL) *explanation = StructureEnum::toName(iter->first) + " surface expected but not found";
277 return false;
278 }
279 if (iter->second.m_numNodes != rhsiter->second.m_numNodes)
280 {
281 if (explanation != NULL) *explanation = "different number of vertices for surface " + StructureEnum::toName(iter->first);
282 return false;
283 }
284 }
285 if (m_parcels.size() != myrhs.m_parcels.size())
286 {
287 if (explanation != NULL) *explanation = "different number of parcels";
288 return false;
289 }
290 for (int64_t i = 0; i < (int64_t)m_parcels.size(); ++i)
291 {
292 if (!m_parcels[i].approximateMatch(myrhs.m_parcels[i], explanation)) return false;
293 }
294 return true;
295 }
296
297 bool CiftiParcelsMap::operator==(const CiftiMappingType& rhs) const
298 {
299 if (rhs.getType() != getType()) return false;
300 const CiftiParcelsMap& myrhs = dynamic_cast<const CiftiParcelsMap&>(rhs);
301 CiftiAssert(!m_ignoreVolSpace && !myrhs.m_ignoreVolSpace);
302 if (m_haveVolumeSpace != myrhs.m_haveVolumeSpace) return false;
303 if (m_haveVolumeSpace && m_volSpace != myrhs.m_volSpace) return false;
304 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
305 for (map<StructureEnum::Enum, SurfaceInfo>::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter)
306 {
307 map<StructureEnum::Enum, SurfaceInfo>::const_iterator rhsiter = myrhs.m_surfInfo.find(iter->first);
308 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
309 if (iter->second.m_numNodes != rhsiter->second.m_numNodes) return false;
310 }
311 return (m_parcels == myrhs.m_parcels);
312 }
313
314 bool CiftiParcelsMap::Parcel::operator==(const CiftiParcelsMap::Parcel& rhs) const
315 {
316 if (m_name != rhs.m_name) return false;
317 if (m_voxelIndices != rhs.m_voxelIndices) return false;
318 return (m_surfaceNodes == rhs.m_surfaceNodes);
319 }
320
321 //same, but don't test name
322 bool CiftiParcelsMap::Parcel::approximateMatch(const CiftiParcelsMap::Parcel& rhs, AString* explanation) const
323 {
324 bool nameMatches = (m_name == rhs.m_name);//for more informative explanations
325 if (m_voxelIndices != rhs.m_voxelIndices)
326 {
327 if (explanation != NULL)
328 {
329 if (nameMatches)
330 {
331 *explanation = "parcel '" + m_name + "' uses different voxels than parcel in other map";
332 } else {
333 *explanation = "parcel '" + m_name + "' uses different voxels than same-index parcel '" + rhs.m_name + "' in other map";
334 }
335 }
336 return false;
337 }
338 if (m_surfaceNodes != rhs.m_surfaceNodes)
339 {
340 if (explanation != NULL)
341 {
342 if (nameMatches)
343 {
344 *explanation = "parcel '" + m_name + "' uses different surface vertices than parcel in other map";
345 } else {
346 *explanation = "parcel '" + m_name + "' uses different surface vertices than same-index parcel '" + rhs.m_name + "' in other map";
347 }
348 }
349 return false;
350 }
351 return true;
352 }
353
354 void CiftiParcelsMap::readXML1(XmlReader& xml)
355 {
356 cerr << "parsing nonstandard parcels mapping type in cifti-1" << endl;
357 clear();
358 m_ignoreVolSpace = true;//cifti-1 has volume space outside the index map
359 vector<Parcel> myParcels;//because we need to add the surfaces first
360 #ifdef CIFTILIB_USE_QT
361 while (xml.readNextStartElement())
362 {
363 QStringRef name = xml.name();
364 if (name == "Surface")
365 {
366 QXmlStreamAttributes attrs = xml.attributes();
367 if (!attrs.hasAttribute("BrainStructure"))
368 {
369 throw CiftiException("Surface element missing required attribute BrainStructure");
370 }
371 bool ok = false;
372 StructureEnum::Enum tempStructure = StructureEnum::fromCiftiName(attrs.value("BrainStructure").toString(), &ok);
373 if (!ok)
374 {
375 throw CiftiException("invalid value for BrainStructure: " + attrs.value("BrainStructure").toString());
376 }
377 if (!attrs.hasAttribute("SurfaceNumberOfNodes"))
378 {
379 throw CiftiException("Surface element missing required attribute SurfaceNumberOfNodes");
380 }
381 int64_t numNodes = attrs.value("SurfaceNumberOfNodes").toString().toLongLong(&ok);
382 if (!ok || numNodes < 1)
383 {
384 throw CiftiException("invalid value for SurfaceNumberOfNodes: " + attrs.value("SurfaceNumberOfNodes").toString());
385 }
386 addSurface(numNodes, tempStructure);//let the standard modification functions do error checking
387 if (xml.readNextStartElement())
388 {
389 throw CiftiException("unexpected element inside Surface: " + xml.name().toString());
390 }
391 } else if (name == "Parcel") {
392 myParcels.push_back(readParcel1(xml));
393 } else {
394 throw CiftiException("unexpected element in parcels map: " + name.toString());
395 }
396 }
397 #else
398 #ifdef CIFTILIB_USE_XMLPP
399 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
400 while(!done && xml.read())
401 {
402 switch (xml.get_node_type())
403 {
404 case XmlReader::Element:
405 {
406 AString name = xml.get_local_name();
407 if (name == "Surface")
408 {
409 vector<AString> mandAttrs(2);
410 mandAttrs[0] = "BrainStructure";
411 mandAttrs[1] = "SurfaceNumberOfNodes";
412 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
413 bool ok = false;
414 StructureEnum::Enum tempStructure = StructureEnum::fromCiftiName(myAttrs.mandatoryVals[0], &ok);
415 if (!ok)
416 {
417 throw CiftiException("invalid value for BrainStructure: " + myAttrs.mandatoryVals[0]);
418 }
419 int64_t numNodes = AString_toInt(myAttrs.mandatoryVals[1], ok);
420 if (!ok || numNodes < 1)
421 {
422 throw CiftiException("invalid value for SurfaceNumberOfNodes: " + myAttrs.mandatoryVals[1]);
423 }
424 addSurface(numNodes, tempStructure);//let the standard modification functions do error checking
425 } else if (name == "Parcel") {
426 myParcels.push_back(readParcel1(xml));
427 } else {
428 throw CiftiException("unexpected element in parcels map: " + name);
429 }
430 break;
431 }
432 case XmlReader::EndElement:
433 done = true;
434 break;
435 default:
436 break;
437 }
438 }
439 #else
440 #error "not implemented"
441 #endif
442 #endif
443 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
444 int64_t numParcels = (int64_t)myParcels.size();
445 for (int64_t i = 0; i < numParcels; ++i)
446 {
447 addParcel(myParcels[i]);
448 }
449 m_ignoreVolSpace = false;//in case there are no voxels, but some will be added later
450 }
451
452 void CiftiParcelsMap::readXML2(XmlReader& xml)
453 {
454 clear();
455 vector<Parcel> myParcels;//because we need to add the surfaces and volume space first
456 #ifdef CIFTILIB_USE_QT
457 while (xml.readNextStartElement())
458 {
459 QStringRef name = xml.name();
460 if (name == "Surface")
461 {
462 QXmlStreamAttributes attrs = xml.attributes();
463 if (!attrs.hasAttribute("BrainStructure"))
464 {
465 throw CiftiException("Surface element missing required attribute BrainStructure");
466 }
467 bool ok = false;
468 StructureEnum::Enum tempStructure = StructureEnum::fromCiftiName(attrs.value("BrainStructure").toString(), &ok);
469 if (!ok)
470 {
471 throw CiftiException("invalid value for BrainStructure: " + attrs.value("BrainStructure").toString());
472 }
473 if (!attrs.hasAttribute("SurfaceNumberOfVertices"))
474 {
475 throw CiftiException("Surface element missing required attribute SurfaceNumberOfVertices");
476 }
477 int64_t numNodes = attrs.value("SurfaceNumberOfVertices").toString().toLongLong(&ok);
478 if (!ok || numNodes < 1)
479 {
480 throw CiftiException("invalid value for SurfaceNumberOfVertices: " + attrs.value("SurfaceNumberOfVertices").toString());
481 }
482 addSurface(numNodes, tempStructure);//let the standard modification functions do error checking
483 if (xml.readNextStartElement())
484 {
485 throw CiftiException("unexpected element inside Surface: " + xml.name().toString());
486 }
487 } else if (name == "Parcel") {
488 myParcels.push_back(readParcel2(xml));
489 if (xml.hasError()) return;
490 } else if (name == "Volume") {
491 if (m_haveVolumeSpace)
492 {
493 throw CiftiException("Volume specified more than once in Parcels mapping type");
494 } else {
495 m_volSpace.readCiftiXML2(xml);
496 if (xml.hasError()) return;
497 m_haveVolumeSpace = true;
498 }
499 } else {
500 throw CiftiException("unexpected element in parcels map: " + name.toString());
501 }
502 }
503 #else
504 #ifdef CIFTILIB_USE_XMLPP
505 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
506 while(!done && xml.read())
507 {
508 switch (xml.get_node_type())
509 {
510 case XmlReader::Element:
511 {
512 AString name = xml.get_local_name();
513 if (name == "Surface")
514 {
515 vector<AString> mandAttrs(2);
516 mandAttrs[0] = "BrainStructure";
517 mandAttrs[1] = "SurfaceNumberOfVertices";
518 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
519 bool ok = false;
520 StructureEnum::Enum tempStructure = StructureEnum::fromCiftiName(myAttrs.mandatoryVals[0], &ok);
521 if (!ok)
522 {
523 throw CiftiException("invalid value for BrainStructure: " + myAttrs.mandatoryVals[0]);
524 }
525 int64_t numNodes = AString_toInt(myAttrs.mandatoryVals[1], ok);
526 if (!ok || numNodes < 1)
527 {
528 throw CiftiException("invalid value for SurfaceNumberOfVertices: " + myAttrs.mandatoryVals[1]);
529 }
530 addSurface(numNodes, tempStructure);//let the standard modification functions do error checking
531 } else if (name == "Parcel") {
532 myParcels.push_back(readParcel2(xml));
533 } else if (name == "Volume") {
534 if (m_haveVolumeSpace)
535 {
536 throw CiftiException("Volume specified more than once in Parcels mapping type");
537 } else {
538 m_volSpace.readCiftiXML2(xml);
539 m_haveVolumeSpace = true;
540 }
541 } else {
542 throw CiftiException("unexpected element in parcels map: " + name);
543 }
544 break;
545 }
546 case XmlReader::EndElement:
547 done = true;
548 break;
549 default:
550 break;
551 }
552 }
553 #else
554 #error "not implemented"
555 #endif
556 #endif
557 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
558 int64_t numParcels = (int64_t)myParcels.size();
559 for (int64_t i = 0; i < numParcels; ++i)
560 {
561 addParcel(myParcels[i]);
562 }
563 }
564
565 CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel1(XmlReader& xml)
566 {
567 Parcel ret;
568 bool haveVoxels = false;
569 vector<AString> mandAttrs(1, "Name");
570 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
571 ret.m_name = myAttrs.mandatoryVals[0];
572 #ifdef CIFTILIB_USE_QT
573 while (xml.readNextStartElement())
574 {
575 QStringRef name = xml.name();
576 if (name == "Nodes")
577 {
578 QXmlStreamAttributes attrs1 = xml.attributes();
579 if (!attrs1.hasAttribute("BrainStructure"))
580 {
581 throw CiftiException("Nodes element missing required attribute BrainStructure");
582 }
583 bool ok = false;
584 StructureEnum::Enum myStructure = StructureEnum::fromCiftiName(attrs1.value("BrainStructure").toString(), &ok);
585 if (!ok)
586 {
587 throw CiftiException("unrecognized value for BrainStructure: " + attrs1.value("BrainStructure").toString());
588 }
589 if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end())
590 {
591 throw CiftiException("Nodes elements may not reuse a BrainStructure within a Parcel");
592 }
593 set<int64_t>& mySet = ret.m_surfaceNodes[myStructure];
594 vector<int64_t> array = readIndexArray(xml);
595 if (xml.hasError()) return ret;
596 int64_t arraySize = (int64_t)array.size();
597 for (int64_t i = 0; i < arraySize; ++i)
598 {
599 if (mySet.find(array[i]) != mySet.end())
600 {
601 throw CiftiException("Nodes elements may not reuse indices");
602 }
603 mySet.insert(array[i]);
604 }
605 } else if (name == "VoxelIndicesIJK") {
606 if (haveVoxels)
607 {
608 throw CiftiException("VoxelIndicesIJK may only appear once in a Parcel");
609 }
610 vector<int64_t> array = readIndexArray(xml);
611 if (xml.hasError()) return ret;
612 int64_t arraySize = (int64_t)array.size();
613 if (arraySize % 3 != 0)
614 {
615 throw CiftiException("number of indices in VoxelIndicesIJK must be a multiple of 3");
616 }
617 for (int64_t index3 = 0; index3 < arraySize; index3 += 3)
618 {
619 VoxelIJK temp(array.data() + index3);
620 if (ret.m_voxelIndices.find(temp) != ret.m_voxelIndices.end())
621 {
622 throw CiftiException("VoxelIndicesIJK elements may not reuse voxels");
623 }
624 ret.m_voxelIndices.insert(temp);
625 }
626 haveVoxels = true;
627 } else {
628 throw CiftiException("unexpected element in Parcel: " + name.toString());
629 }
630 }
631 #else
632 #ifdef CIFTILIB_USE_XMLPP
633 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
634 while(!done && xml.read())
635 {
636 switch (xml.get_node_type())
637 {
638 case XmlReader::Element:
639 {
640 AString name = xml.get_local_name();
641 if (name == "Nodes")
642 {
643 vector<AString> mandAttrs1(1, "BrainStructure");
644 XmlAttributesResult myAttrs1 = XmlReader_parseAttributes(xml, mandAttrs1);
645 bool ok = false;
646 StructureEnum::Enum myStructure = StructureEnum::fromCiftiName(myAttrs1.mandatoryVals[0], &ok);
647 if (!ok)
648 {
649 throw CiftiException("unrecognized value for BrainStructure: " + myAttrs1.mandatoryVals[0]);
650 }
651 if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end())
652 {
653 throw CiftiException("Nodes elements may not reuse a BrainStructure within a Parcel");
654 }
655 set<int64_t>& mySet = ret.m_surfaceNodes[myStructure];
656 vector<int64_t> array = readIndexArray(xml);
657 int64_t arraySize = (int64_t)array.size();
658 for (int64_t i = 0; i < arraySize; ++i)
659 {
660 if (mySet.find(array[i]) != mySet.end())
661 {
662 throw CiftiException("Nodes elements may not reuse indices");
663 }
664 mySet.insert(array[i]);
665 }
666 } else if (name == "VoxelIndicesIJK") {
667 if (haveVoxels)
668 {
669 throw CiftiException("VoxelIndicesIJK may only appear once in a Parcel");
670 }
671 vector<int64_t> array = readIndexArray(xml);
672 int64_t arraySize = (int64_t)array.size();
673 if (arraySize % 3 != 0)
674 {
675 throw CiftiException("number of indices in VoxelIndicesIJK must be a multiple of 3");
676 }
677 for (int64_t index3 = 0; index3 < arraySize; index3 += 3)
678 {
679 VoxelIJK temp(array.data() + index3);
680 if (ret.m_voxelIndices.find(temp) != ret.m_voxelIndices.end())
681 {
682 throw CiftiException("VoxelIndicesIJK elements may not reuse voxels");
683 }
684 ret.m_voxelIndices.insert(temp);
685 }
686 haveVoxels = true;
687 } else {
688 throw CiftiException("unexpected element in Parcel: " + name);
689 }
690 break;
691 }
692 case XmlReader::EndElement:
693 done = true;
694 break;
695 default:
696 break;
697 }
698 }
699 #else
700 #error "not implemented"
701 #endif
702 #endif
703 CiftiAssert(XmlReader_checkEndElement(xml, "Parcel"));
704 return ret;
705 }
706
707 CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel2(XmlReader& xml)
708 {
709 Parcel ret;
710 bool haveVoxels = false;
711 vector<AString> mandAttrs(1, "Name");
712 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
713 ret.m_name = myAttrs.mandatoryVals[0];
714 #ifdef CIFTILIB_USE_QT
715 while (xml.readNextStartElement())
716 {
717 QStringRef name = xml.name();
718 if (name == "Vertices")
719 {
720 QXmlStreamAttributes attrs1 = xml.attributes();
721 if (!attrs1.hasAttribute("BrainStructure"))
722 {
723 throw CiftiException("Vertices element missing required attribute BrainStructure");
724 }
725 bool ok = false;
726 StructureEnum::Enum myStructure = StructureEnum::fromCiftiName(attrs1.value("BrainStructure").toString(), &ok);
727 if (!ok)
728 {
729 throw CiftiException("unrecognized value for BrainStructure: " + attrs1.value("BrainStructure").toString());
730 }
731 if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end())
732 {
733 throw CiftiException("Vertices elements may not reuse a BrainStructure within a Parcel");
734 }
735 set<int64_t>& mySet = ret.m_surfaceNodes[myStructure];
736 vector<int64_t> array = readIndexArray(xml);
737 if (xml.hasError()) return ret;
738 int64_t arraySize = (int64_t)array.size();
739 for (int64_t i = 0; i < arraySize; ++i)
740 {
741 if (mySet.find(array[i]) != mySet.end())
742 {
743 throw CiftiException("Vertices elements may not reuse indices");
744 }
745 mySet.insert(array[i]);
746 }
747 } else if (name == "VoxelIndicesIJK") {
748 if (haveVoxels)
749 {
750 throw CiftiException("VoxelIndicesIJK may only appear once in a Parcel");
751 }
752 vector<int64_t> array = readIndexArray(xml);
753 if (xml.hasError()) return ret;
754 int64_t arraySize = (int64_t)array.size();
755 if (arraySize % 3 != 0)
756 {
757 throw CiftiException("number of indices in VoxelIndicesIJK must be a multiple of 3");
758 }
759 for (int64_t index3 = 0; index3 < arraySize; index3 += 3)
760 {
761 VoxelIJK temp(array.data() + index3);
762 if (ret.m_voxelIndices.find(temp) != ret.m_voxelIndices.end())
763 {
764 throw CiftiException("VoxelIndicesIJK elements may not reuse voxels");
765 }
766 ret.m_voxelIndices.insert(temp);
767 }
768 haveVoxels = true;
769 } else {
770 throw CiftiException("unexpected element in Parcel: " + name.toString());
771 }
772 }
773 #else
774 #ifdef CIFTILIB_USE_XMLPP
775 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
776 while(!done && xml.read())
777 {
778 switch (xml.get_node_type())
779 {
780 case XmlReader::Element:
781 {
782 AString name = xml.get_local_name();
783 if (name == "Vertices")
784 {
785 vector<AString> mandAttrs1(1, "BrainStructure");
786 XmlAttributesResult myAttrs1 = XmlReader_parseAttributes(xml, mandAttrs1);
787 bool ok = false;
788 StructureEnum::Enum myStructure = StructureEnum::fromCiftiName(myAttrs1.mandatoryVals[0], &ok);
789 if (!ok)
790 {
791 throw CiftiException("unrecognized value for BrainStructure: " + myAttrs1.mandatoryVals[0]);
792 }
793 if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end())
794 {
795 throw CiftiException("Vertices elements may not reuse a BrainStructure within a Parcel");
796 }
797 set<int64_t>& mySet = ret.m_surfaceNodes[myStructure];
798 vector<int64_t> array = readIndexArray(xml);
799 int64_t arraySize = (int64_t)array.size();
800 for (int64_t i = 0; i < arraySize; ++i)
801 {
802 if (mySet.find(array[i]) != mySet.end())
803 {
804 throw CiftiException("Vertices elements may not reuse indices");
805 }
806 mySet.insert(array[i]);
807 }
808 } else if (name == "VoxelIndicesIJK") {
809 if (haveVoxels)
810 {
811 throw CiftiException("VoxelIndicesIJK may only appear once in a Parcel");
812 }
813 vector<int64_t> array = readIndexArray(xml);
814 int64_t arraySize = (int64_t)array.size();
815 if (arraySize % 3 != 0)
816 {
817 throw CiftiException("number of indices in VoxelIndicesIJK must be a multiple of 3");
818 }
819 for (int64_t index3 = 0; index3 < arraySize; index3 += 3)
820 {
821 VoxelIJK temp(array.data() + index3);
822 if (ret.m_voxelIndices.find(temp) != ret.m_voxelIndices.end())
823 {
824 throw CiftiException("VoxelIndicesIJK elements may not reuse voxels");
825 }
826 ret.m_voxelIndices.insert(temp);
827 }
828 haveVoxels = true;
829 } else {
830 throw CiftiException("unexpected element in Parcel: " + name);
831 }
832 break;
833 }
834 case XmlReader::EndElement:
835 done = true;
836 break;
837 default:
838 break;
839 }
840 }
841 #else
842 #error "not implemented"
843 #endif
844 #endif
845 CiftiAssert(XmlReader_checkEndElement(xml, "Parcel"));
846 return ret;
847 }
848
849 vector<int64_t> CiftiParcelsMap::readIndexArray(XmlReader& xml)
850 {
851 vector<int64_t> ret;
852 AString text = XmlReader_readElementText(xml);//raises error if it encounters a start element
853 #ifdef CIFTILIB_USE_QT
854 if (xml.hasError()) return ret;
855 #endif
856 vector<AString> separated = AString_split_whitespace(text);
857 int64_t numElems = separated.size();
858 ret.reserve(numElems);
859 for (int64_t i = 0; i < numElems; ++i)
860 {
861 bool ok = false;
862 ret.push_back(AString_toInt(separated[i], ok));
863 if (!ok)
864 {
865 throw CiftiException("found noninteger in index array: " + separated[i]);
866 }
867 if (ret.back() < 0)
868 {
869 throw CiftiException("found negative integer in index array: " + separated[i]);
870 }
871 }
872 return ret;
873 }
874
875 void CiftiParcelsMap::writeXML1(XmlWriter& xml) const
876 {
877 CiftiAssert(!m_ignoreVolSpace);
878 cerr << "writing nonstandard parcels mapping type in cifti-1" << endl;
879 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_PARCELS");
880 for (map<StructureEnum::Enum, SurfaceInfo>::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter)
881 {
882 xml.writeStartElement("Surface");
883 xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first));
884 xml.writeAttribute("SurfaceNumberOfNodes", AString_number(iter->second.m_numNodes));
885 xml.writeEndElement();
886 }
887 int64_t numParcels = m_parcels.size();
888 for (int64_t i = 0; i < numParcels; ++i)
889 {
890 xml.writeStartElement("Parcel");
891 xml.writeAttribute("Name", m_parcels[i].m_name);
892 int64_t numVoxels = (int64_t)m_parcels[i].m_voxelIndices.size();
893 if (numVoxels != 0)
894 {
895 xml.writeStartElement("VoxelIndicesIJK");
896 for (set<VoxelIJK>::const_iterator iter = m_parcels[i].m_voxelIndices.begin(); iter != m_parcels[i].m_voxelIndices.end(); ++iter)
897 {
898 xml.writeCharacters(AString_number(iter->m_ijk[0]) + " " + AString_number(iter->m_ijk[1]) + " " + AString_number(iter->m_ijk[2]) + "\n");
899 }
900 xml.writeEndElement();
901 }
902 for (map<StructureEnum::Enum, set<int64_t> >::const_iterator iter = m_parcels[i].m_surfaceNodes.begin(); iter != m_parcels[i].m_surfaceNodes.end(); ++iter)
903 {
904 if (iter->second.size() != 0)//prevent writing empty elements, regardless
905 {
906 xml.writeStartElement("Nodes");
907 xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first));
908 set<int64_t>::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
909 xml.writeCharacters(AString_number(*iter2));
910 ++iter2;
911 for (; iter2 != iter->second.end(); ++iter2)
912 {
913 xml.writeCharacters(" " + AString_number(*iter2));
914 }
915 xml.writeEndElement();
916 }
917 }
918 xml.writeEndElement();
919 }
920 }
921
922 void CiftiParcelsMap::writeXML2(XmlWriter& xml) const
923 {
924 CiftiAssert(!m_ignoreVolSpace);
925 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_PARCELS");
926 if (hasVolumeData())//could be m_haveVolumeSpace if we want to be able to write a volspace without having voxels, but that seems silly
927 {
928 m_volSpace.writeCiftiXML2(xml);
929 }
930 for (map<StructureEnum::Enum, SurfaceInfo>::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter)
931 {
932 xml.writeStartElement("Surface");
933 xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first));
934 xml.writeAttribute("SurfaceNumberOfVertices", AString_number(iter->second.m_numNodes));
935 xml.writeEndElement();
936 }
937 int64_t numParcels = m_parcels.size();
938 for (int64_t i = 0; i < numParcels; ++i)
939 {
940 xml.writeStartElement("Parcel");
941 xml.writeAttribute("Name", m_parcels[i].m_name);
942 int64_t numVoxels = (int64_t)m_parcels[i].m_voxelIndices.size();
943 if (numVoxels != 0)
944 {
945 xml.writeStartElement("VoxelIndicesIJK");
946 for (set<VoxelIJK>::const_iterator iter = m_parcels[i].m_voxelIndices.begin(); iter != m_parcels[i].m_voxelIndices.end(); ++iter)
947 {
948 xml.writeCharacters(AString_number(iter->m_ijk[0]) + " " + AString_number(iter->m_ijk[1]) + " " + AString_number(iter->m_ijk[2]) + "\n");
949 }
950 xml.writeEndElement();
951 }
952 for (map<StructureEnum::Enum, set<int64_t> >::const_iterator iter = m_parcels[i].m_surfaceNodes.begin(); iter != m_parcels[i].m_surfaceNodes.end(); ++iter)
953 {
954 if (iter->second.size() != 0)//prevent writing empty elements, regardless
955 {
956 xml.writeStartElement("Vertices");
957 xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first));
958 set<int64_t>::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
959 xml.writeCharacters(AString_number(*iter2));
960 ++iter2;
961 for (; iter2 != iter->second.end(); ++iter2)
962 {
963 xml.writeCharacters(" " + AString_number(*iter2));
964 }
965 xml.writeEndElement();
966 }
967 }
968 xml.writeEndElement();
969 }
970 }
0 #ifndef __CIFTI_PARCELS_MAP_H__
1 #define __CIFTI_PARCELS_MAP_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiMappingType.h"
31
32 #include "Common/Compact3DLookup.h"
33 #include "StructureEnum.h"
34 #include "VolumeSpace.h"
35 #include "Common/VoxelIJK.h"
36
37 #include <map>
38 #include <set>
39 #include <vector>
40
41 namespace cifti
42 {
43 class CiftiParcelsMap : public CiftiMappingType
44 {
45 public:
46 struct Parcel
47 {
48 std::map<StructureEnum::Enum, std::set<int64_t> > m_surfaceNodes;
49 std::set<VoxelIJK> m_voxelIndices;
50 AString m_name;
51 bool operator==(const Parcel& rhs) const;
52 bool operator!=(const Parcel& rhs) const { return !((*this) == rhs); }
53 bool approximateMatch(const Parcel& rhs, AString* explanation = NULL) const;
54 };
55 bool hasVolumeData() const;
56 bool hasSurface(const StructureEnum::Enum& structure) const;//only checks whether surface has been added/read
57 bool hasSurfaceData(const StructureEnum::Enum& structure) const;
58 const VolumeSpace& getVolumeSpace() const;
59 int64_t getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const;
60 int64_t getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const;
61 int64_t getIndexForVoxel(const int64_t* ijk) const;
62 int64_t getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k) const;
63 std::vector<StructureEnum::Enum> getParcelSurfaceStructures() const;
64 const std::vector<Parcel>& getParcels() const { return m_parcels; }
65
66 CiftiParcelsMap() { m_haveVolumeSpace = false; m_ignoreVolSpace = false; }
67 void addSurface(const int64_t& numberOfNodes, const StructureEnum::Enum& structure);
68 void setVolumeSpace(const VolumeSpace& space);
69 void addParcel(const Parcel& parcel);
70 void clear();
71
72 CiftiMappingType* clone() const { return new CiftiParcelsMap(*this); }
73 MappingType getType() const { return PARCELS; }
74 int64_t getLength() const { return m_parcels.size(); }
75 bool operator==(const CiftiMappingType& rhs) const;
76 bool approximateMatch(const CiftiMappingType& rhs, AString* explanation = NULL) const;
77 void readXML1(XmlReader& xml);
78 void readXML2(XmlReader& xml);
79 void writeXML1(XmlWriter& xml) const;
80 void writeXML2(XmlWriter& xml) const;
81 private:
82 std::vector<Parcel> m_parcels;
83 VolumeSpace m_volSpace;
84 bool m_haveVolumeSpace, m_ignoreVolSpace;//second is needed for parsing cifti-1;
85 struct SurfaceInfo
86 {
87 int64_t m_numNodes;
88 std::vector<int64_t> m_lookup;
89 };
90 Compact3DLookup<int64_t> m_volLookup;
91 std::map<StructureEnum::Enum, SurfaceInfo> m_surfInfo;
92 static Parcel readParcel1(XmlReader& xml);
93 static Parcel readParcel2(XmlReader& xml);
94 static std::vector<int64_t> readIndexArray(XmlReader& xml);
95 };
96 }
97
98 #endif //__CIFTI_PARCELS_MAP_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiScalarsMap.h"
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/CiftiException.h"
31
32 #include <iostream>
33
34 using namespace std;
35 using namespace cifti;
36
37 void CiftiScalarsMap::clear()
38 {
39 m_maps.clear();
40 }
41
42 const MetaData& CiftiScalarsMap::getMapMetadata(const int64_t& index) const
43 {
44 CiftiAssertVectorIndex(m_maps, index);
45 return m_maps[index].m_metaData;
46 }
47
48 const AString& CiftiScalarsMap::getMapName(const int64_t& index) const
49 {
50 CiftiAssertVectorIndex(m_maps, index);
51 return m_maps[index].m_name;
52 }
53
54 void CiftiScalarsMap::setMapMetadata(const int64_t& index, const MetaData& mdIn)
55 {
56 CiftiAssertVectorIndex(m_maps, index);
57 m_maps[index].m_metaData = mdIn;
58 }
59
60 void CiftiScalarsMap::setMapName(const int64_t& index, const AString& mapName)
61 {
62 CiftiAssertVectorIndex(m_maps, index);
63 m_maps[index].m_name = mapName;
64 }
65
66 void CiftiScalarsMap::setLength(const int64_t& length)
67 {
68 CiftiAssert(length > 0);
69 m_maps.resize(length);
70 }
71
72 bool CiftiScalarsMap::approximateMatch(const CiftiMappingType& rhs, AString* explanation) const
73 {
74 switch (rhs.getType())
75 {
76 case SCALARS:
77 case SERIES://maybe?
78 case LABELS:
79 if (getLength() != rhs.getLength())
80 {
81 if (explanation != NULL) *explanation = "mappings have different length";
82 return false;
83 } else return true;
84 default:
85 if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType());
86 return false;
87 }
88 }
89
90 bool CiftiScalarsMap::operator==(const CiftiMappingType& rhs) const
91 {
92 if (rhs.getType() != getType()) return false;
93 const CiftiScalarsMap& myrhs = dynamic_cast<const CiftiScalarsMap&>(rhs);
94 return (m_maps == myrhs.m_maps);
95 }
96
97 bool CiftiScalarsMap::ScalarMap::operator==(const CiftiScalarsMap::ScalarMap& rhs) const
98 {
99 if (m_name != rhs.m_name) return false;
100 return (m_metaData == rhs.m_metaData);
101 }
102
103 void CiftiScalarsMap::readXML1(XmlReader& xml)
104 {
105 cerr << "parsing nonstandard scalars mapping type in cifti-1" << endl;
106 clear();
107 #ifdef CIFTILIB_USE_QT
108 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
109 {
110 switch (xml.tokenType())
111 {
112 case QXmlStreamReader::StartElement:
113 {
114 if (xml.name() != "NamedMap")
115 {
116 throw CiftiException("unexpected element in scalars map: " + xml.name().toString());
117 }
118 m_maps.push_back(ScalarMap());
119 m_maps.back().readXML1(xml);
120 if (xml.hasError()) return;
121 break;
122 }
123 default:
124 break;
125 }
126 }
127 #else
128 #ifdef CIFTILIB_USE_XMLPP
129 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
130 while(!done && xml.read())
131 {
132 switch (xml.get_node_type())
133 {
134 case XmlReader::Element:
135 {
136 AString name = xml.get_local_name();
137 if (name == "NamedMap")
138 {
139 m_maps.push_back(ScalarMap());
140 m_maps.back().readXML1(xml);
141 } else {
142 throw CiftiException("unexpected element in scalars map: " + name);
143 }
144 break;
145 }
146 case XmlReader::EndElement:
147 done = true;
148 break;
149 default:
150 break;
151 }
152 }
153 #else
154 #error "not implemented"
155 #endif
156 #endif
157 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
158 }
159
160 void CiftiScalarsMap::readXML2(XmlReader& xml)
161 {
162 clear();
163 #ifdef CIFTILIB_USE_QT
164 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
165 {
166 switch (xml.tokenType())
167 {
168 case QXmlStreamReader::StartElement:
169 {
170 if (xml.name() != "NamedMap")
171 {
172 throw CiftiException("unexpected element in scalars map: " + xml.name().toString());
173 }
174 m_maps.push_back(ScalarMap());
175 m_maps.back().readXML2(xml);
176 if (xml.hasError()) return;
177 break;
178 }
179 default:
180 break;
181 }
182 }
183 #else
184 #ifdef CIFTILIB_USE_XMLPP
185 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
186 while(!done && xml.read())
187 {
188 switch (xml.get_node_type())
189 {
190 case XmlReader::Element:
191 {
192 AString name = xml.get_local_name();
193 if (name == "NamedMap")
194 {
195 m_maps.push_back(ScalarMap());
196 m_maps.back().readXML2(xml);
197 } else {
198 throw CiftiException("unexpected element in scalars map: " + name);
199 }
200 break;
201 }
202 case XmlReader::EndElement:
203 done = true;
204 break;
205 default:
206 break;
207 }
208 }
209 #else
210 #error "not implemented"
211 #endif
212 #endif
213 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
214 }
215
216 void CiftiScalarsMap::ScalarMap::readXML1(XmlReader& xml)
217 {
218 bool haveName = false, haveMetaData = false;
219 #ifdef CIFTILIB_USE_QT
220 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
221 {
222 switch (xml.tokenType())
223 {
224 case QXmlStreamReader::StartElement:
225 {
226 QStringRef name = xml.name();
227 if (name == "MetaData")
228 {
229 if (haveMetaData)
230 {
231 throw CiftiException("MetaData specified multiple times in one NamedMap");
232 }
233 m_metaData.readCiftiXML1(xml);
234 if (xml.hasError()) return;
235 haveMetaData = true;
236 } else if (name == "MapName") {
237 if (haveName)
238 {
239 throw CiftiException("MapName specified multiple times in one NamedMap");
240 }
241 m_name = xml.readElementText();//raises error if element encountered
242 if (xml.hasError()) return;
243 haveName = true;
244 } else {
245 throw CiftiException("unexpected element in NamedMap: " + name.toString());
246 }
247 break;
248 }
249 default:
250 break;
251 }
252 }
253 #else
254 #ifdef CIFTILIB_USE_XMLPP
255 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
256 while(!done && xml.read())
257 {
258 switch (xml.get_node_type())
259 {
260 case XmlReader::Element:
261 {
262 AString name = xml.get_local_name();
263 if (name == "MetaData")
264 {
265 if (haveMetaData)
266 {
267 throw CiftiException("MetaData specified multiple times in one NamedMap");
268 }
269 m_metaData.readCiftiXML1(xml);
270 haveMetaData = true;
271 } else if (name == "MapName") {
272 if (haveName)
273 {
274 throw CiftiException("MapName specified multiple times in one NamedMap");
275 }
276 m_name = XmlReader_readElementText(xml);//throws if element encountered
277 haveName = true;
278 } else {
279 throw CiftiException("unexpected element in NamedMap: " + name);
280 }
281 break;
282 }
283 case XmlReader::EndElement:
284 done = true;
285 break;
286 default:
287 break;
288 }
289 }
290 #else
291 #error "not implemented"
292 #endif
293 #endif
294 CiftiAssert(XmlReader_checkEndElement(xml, "NamedMap"));
295 if (!haveName)
296 {
297 throw CiftiException("NamedMap missing required child element MapName");
298 }
299 }
300
301 void CiftiScalarsMap::ScalarMap::readXML2(XmlReader& xml)
302 {
303 bool haveName = false, haveMetaData = false;
304 #ifdef CIFTILIB_USE_QT
305 for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext())
306 {
307 switch (xml.tokenType())
308 {
309 case QXmlStreamReader::StartElement:
310 {
311 QStringRef name = xml.name();
312 if (name == "MetaData")
313 {
314 if (haveMetaData)
315 {
316 throw CiftiException("MetaData specified multiple times in one NamedMap");
317 }
318 m_metaData.readCiftiXML2(xml);
319 if (xml.hasError()) return;
320 haveMetaData = true;
321 } else if (name == "MapName") {
322 if (haveName)
323 {
324 throw CiftiException("MapName specified multiple times in one NamedMap");
325 }
326 m_name = xml.readElementText();//raises error if element encountered
327 if (xml.hasError()) return;
328 haveName = true;
329 } else {
330 throw CiftiException("unexpected element in NamedMap: " + name.toString());
331 }
332 break;
333 }
334 default:
335 break;
336 }
337 }
338 #else
339 #ifdef CIFTILIB_USE_XMLPP
340 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
341 while(!done && xml.read())
342 {
343 switch (xml.get_node_type())
344 {
345 case XmlReader::Element:
346 {
347 AString name = xml.get_local_name();
348 if (name == "MetaData")
349 {
350 if (haveMetaData)
351 {
352 throw CiftiException("MetaData specified multiple times in one NamedMap");
353 }
354 m_metaData.readCiftiXML2(xml);
355 haveMetaData = true;
356 } else if (name == "MapName") {
357 if (haveName)
358 {
359 throw CiftiException("MapName specified multiple times in one NamedMap");
360 }
361 m_name = XmlReader_readElementText(xml);//throws if element encountered
362 haveName = true;
363 } else {
364 throw CiftiException("unexpected element in NamedMap: " + name);
365 }
366 break;
367 }
368 case XmlReader::EndElement:
369 done = true;
370 break;
371 default:
372 break;
373 }
374 }
375 #else
376 #error "not implemented"
377 #endif
378 #endif
379 CiftiAssert(XmlReader_checkEndElement(xml, "NamedMap"));
380 if (!haveName)
381 {
382 throw CiftiException("NamedMap missing required child element MapName");
383 }
384 }
385
386 void CiftiScalarsMap::writeXML1(XmlWriter& xml) const
387 {
388 cerr << "writing nonstandard scalars mapping type in cifti-1" << endl;
389 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_SCALARS");
390 int64_t numMaps = (int64_t)m_maps.size();
391 for (int64_t i = 0; i < numMaps; ++i)
392 {
393 xml.writeStartElement("NamedMap");
394 xml.writeTextElement("MapName", m_maps[i].m_name);
395 m_maps[i].m_metaData.writeCiftiXML1(xml);
396 xml.writeEndElement();
397 }
398 }
399
400 void CiftiScalarsMap::writeXML2(XmlWriter& xml) const
401 {
402 int64_t numMaps = (int64_t)m_maps.size();
403 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_SCALARS");
404 for (int64_t i = 0; i < numMaps; ++i)
405 {
406 xml.writeStartElement("NamedMap");
407 xml.writeTextElement("MapName", m_maps[i].m_name);
408 m_maps[i].m_metaData.writeCiftiXML1(xml);
409 xml.writeEndElement();
410 }
411 }
0 #ifndef __CIFTI_SCALARS_MAP_H__
1 #define __CIFTI_SCALARS_MAP_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiMappingType.h"
31
32 #include "MetaData.h"
33
34 #include <map>
35 #include <vector>
36
37 namespace cifti
38 {
39 class CiftiScalarsMap : public CiftiMappingType
40 {
41 public:
42 CiftiScalarsMap() { }
43 CiftiScalarsMap(const CiftiScalarsMap& rhs) { m_maps = rhs.m_maps; }
44 CiftiScalarsMap& operator=(const CiftiScalarsMap& rhs) { m_maps = rhs.m_maps; return *this; }
45 explicit CiftiScalarsMap(const int64_t& length) { setLength(length); }
46
47 const MetaData& getMapMetadata(const int64_t& index) const;
48 const AString& getMapName(const int64_t& index) const;
49
50 void setMapMetadata(const int64_t& index, const MetaData& mdIn);
51 void setMapName(const int64_t& index, const AString& mapName);
52 void setLength(const int64_t& length);
53 void clear();
54
55 CiftiMappingType* clone() const { return new CiftiScalarsMap(*this); }
56 MappingType getType() const { return SCALARS; }
57 int64_t getLength() const { return m_maps.size(); }
58 bool operator==(const CiftiMappingType& rhs) const;
59 bool approximateMatch(const CiftiMappingType& rhs, AString* explanation = NULL) const;
60 void readXML1(XmlReader& xml);
61 void readXML2(XmlReader& xml);
62 void writeXML1(XmlWriter& xml) const;
63 void writeXML2(XmlWriter& xml) const;
64 private:
65 struct ScalarMap
66 {
67 AString m_name;
68 MetaData m_metaData;
69 bool operator==(const ScalarMap& rhs) const;
70 void readXML1(XmlReader& xml);
71 void readXML2(XmlReader& xml);
72 };
73 std::vector<ScalarMap> m_maps;
74 };
75 }
76
77 #endif //__CIFTI_SCALARS_MAP_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiSeriesMap.h"
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/CiftiException.h"
31
32 #include <cmath>
33 #include <iostream>
34
35 using namespace cifti;
36 using namespace std;
37
38 void CiftiSeriesMap::setLength(const int64_t& length)
39 {
40 CiftiAssert(length > 0);
41 m_length = length;
42 }
43
44 CiftiSeriesMap::Unit CiftiSeriesMap::stringToUnit(const AString& string, bool& ok)
45 {
46 ok = true;
47 if (string == "SECOND")
48 {
49 return SECOND;
50 } else if (string == "HERTZ") {
51 return HERTZ;
52 } else if (string == "METER") {
53 return METER;
54 } else if (string == "RADIAN") {
55 return RADIAN;
56 }
57 ok = false;
58 return SECOND;
59 }
60
61 AString CiftiSeriesMap::unitToString(const CiftiSeriesMap::Unit& theUnit)
62 {
63 switch (theUnit)
64 {
65 case SECOND:
66 return "SECOND";
67 case HERTZ:
68 return "HERTZ";
69 case METER:
70 return "METER";
71 case RADIAN:
72 return "RADIAN";
73 }
74 CiftiAssert(false);
75 return "UNKNOWN";
76 }
77
78 vector<CiftiSeriesMap::Unit> CiftiSeriesMap::getAllUnits()
79 {
80 vector<Unit> ret;
81 ret.push_back(SECOND);
82 ret.push_back(HERTZ);
83 ret.push_back(METER);
84 ret.push_back(RADIAN);
85 return ret;
86 }
87
88 void CiftiSeriesMap::readXML1(XmlReader& xml)
89 {
90 vector<AString> mandAttrs(2), optAttrs(1, "TimeStart");
91 mandAttrs[0] = "TimeStep";
92 mandAttrs[1] = "TimeStepUnits";
93 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs, optAttrs);
94 float newStart = 0.0f, newStep = -1.0f, mult = 0.0f;
95 bool ok = false;
96 if (myAttrs.mandatoryVals[1] == "NIFTI_UNITS_SEC")
97 {
98 mult = 1.0f;
99 } else if (myAttrs.mandatoryVals[1] == "NIFTI_UNITS_MSEC") {
100 mult = 0.001f;
101 } else if (myAttrs.mandatoryVals[1] == "NIFTI_UNITS_USEC") {
102 mult = 0.000001f;
103 } else {
104 throw CiftiException("unrecognized value for TimeStepUnits: " + myAttrs.mandatoryVals[1]);
105 }
106 if (myAttrs.optionalVals[0].present)//optional and nonstandard
107 {
108 newStart = mult * AString_toFloat(myAttrs.optionalVals[0].value, ok);
109 if (!ok)
110 {
111 throw CiftiException("unrecognized value for TimeStart: " + myAttrs.optionalVals[0].value);
112 }
113 }
114 newStep = mult * AString_toFloat(myAttrs.mandatoryVals[0], ok);
115 if (!ok)
116 {
117 throw CiftiException("unrecognized value for TimeStep: " + myAttrs.mandatoryVals[0]);
118 }
119 #ifdef CIFTILIB_USE_QT
120 if (xml.readNextStartElement())
121 {
122 throw CiftiException("unexpected element in timepoints map: " + xml.name().toString());
123 }
124 #else
125 #ifdef CIFTILIB_USE_XMLPP
126 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
127 while(!done && xml.read())
128 {
129 switch (xml.get_node_type())
130 {
131 case XmlReader::Element:
132 {
133 AString name = xml.get_local_name();
134 throw CiftiException("unexpected element in timepoints map: " + name);
135 break;
136 }
137 case XmlReader::EndElement:
138 done = true;
139 break;
140 default:
141 break;
142 }
143 }
144 #else
145 #error "not implemented"
146 #endif
147 #endif
148 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
149 m_length = -1;//cifti-1 doesn't know length in xml, must be set by checking the matrix
150 m_start = newStart;
151 m_step = newStep;
152 m_unit = SECOND;
153 }
154
155 void CiftiSeriesMap::readXML2(XmlReader& xml)
156 {
157 vector<AString> mandAttrs(5);
158 mandAttrs[0] = "SeriesStep";
159 mandAttrs[1] = "SeriesUnit";
160 mandAttrs[2] = "SeriesExponent";
161 mandAttrs[3] = "SeriesStart";
162 mandAttrs[4] = "NumberOfSeriesPoints";
163 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
164 float newStart = 0.0f, newStep = -1.0f, mult = 0.0f;
165 int64_t newLength = -1;
166 Unit newUnit;
167 bool ok = false;
168 if (myAttrs.mandatoryVals[1] == "HERTZ")
169 {
170 newUnit = HERTZ;
171 } else if (myAttrs.mandatoryVals[1] == "METER") {
172 newUnit = METER;
173 } else if (myAttrs.mandatoryVals[1] == "RADIAN") {
174 newUnit = RADIAN;
175 } else if (myAttrs.mandatoryVals[1] == "SECOND") {
176 newUnit = SECOND;
177 } else {
178 throw CiftiException("unrecognized value for SeriesUnit: " + myAttrs.mandatoryVals[1]);
179 }
180 int exponent = AString_toInt(myAttrs.mandatoryVals[2], ok);
181 if (!ok)
182 {
183 throw CiftiException("unrecognized value for SeriesExponent: " + myAttrs.mandatoryVals[2]);
184 }
185 mult = pow(10.0f, exponent);
186 newStart = mult * AString_toFloat(myAttrs.mandatoryVals[3], ok);
187 if (!ok)
188 {
189 throw CiftiException("unrecognized value for SeriesStart: " + myAttrs.mandatoryVals[3]);
190 }
191 newStep = mult * AString_toFloat(myAttrs.mandatoryVals[0], ok);
192 if (!ok)
193 {
194 throw CiftiException("unrecognized value for SeriesStep: " + myAttrs.mandatoryVals[0]);
195 }
196 newLength = AString_toInt(myAttrs.mandatoryVals[4], ok);
197 if (!ok)
198 {
199 throw CiftiException("unrecognized value for NumberOfSeriesPoints: " + myAttrs.mandatoryVals[4]);
200 }
201 if (newLength < 1)
202 {
203 throw CiftiException("NumberOfSeriesPoints must be positive");
204 }
205 #ifdef CIFTILIB_USE_QT
206 if (xml.readNextStartElement())
207 {
208 throw CiftiException("unexpected element in series map: " + xml.name().toString());
209 }
210 #else
211 #ifdef CIFTILIB_USE_XMLPP
212 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
213 while(!done && xml.read())
214 {
215 switch (xml.get_node_type())
216 {
217 case XmlReader::Element:
218 {
219 AString name = xml.get_local_name();
220 throw CiftiException("unexpected element in series map: " + name);
221 break;
222 }
223 case XmlReader::EndElement:
224 done = true;
225 break;
226 default:
227 break;
228 }
229 }
230 #else
231 #error "not implemented"
232 #endif
233 #endif
234 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
235 m_length = newLength;
236 m_start = newStart;
237 m_step = newStep;
238 m_unit = newUnit;
239 }
240
241 void CiftiSeriesMap::writeXML1(XmlWriter& xml) const
242 {
243 CiftiAssert(m_length != -1);
244 if (m_unit != SECOND)
245 {
246 cerr << "WARNING: changing series units to seconds for CIFTI-1 XML" << endl;
247 }
248 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_TIME_POINTS");
249 float mult = 1.0f;
250 AString unitString = "NIFTI_UNITS_SEC";
251 float test = m_step;
252 if (test == 0.0f) test = m_start;
253 if (test != 0.0f)
254 {
255 if (abs(test) < 0.00005f)
256 {
257 mult = 1000000.0f;
258 unitString = "NIFTI_UNITS_USEC";
259 } else if (abs(test) < 0.05f) {
260 mult = 1000.0f;
261 unitString = "NIFTI_UNITS_MSEC";
262 }
263 }
264 xml.writeAttribute("TimeStepUnits", unitString);
265 xml.writeAttribute("TimeStart", AString_number_fixed(mult * m_start, 7));//even though it is nonstandard, write it, always
266 xml.writeAttribute("TimeStep", AString_number_fixed(mult * m_step, 7));
267 }
268
269 void CiftiSeriesMap::writeXML2(XmlWriter& xml) const
270 {
271 CiftiAssert(m_length != -1);
272 xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_SERIES");
273 int exponent = 0;
274 float test = m_step;
275 if (test == 0.0f) test = m_start;
276 if (test != 0.0f)
277 {
278 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]
279 }
280 float mult = pow(10.0f, -exponent);
281 AString unitString;
282 switch (m_unit)
283 {
284 case HERTZ:
285 unitString = "HERTZ";
286 break;
287 case METER:
288 unitString = "METER";
289 break;
290 case RADIAN:
291 unitString = "RADIAN";
292 break;
293 case SECOND:
294 unitString = "SECOND";
295 break;
296 }
297 xml.writeAttribute("NumberOfSeriesPoints", AString_number(m_length));
298 xml.writeAttribute("SeriesExponent", AString_number(exponent));
299 xml.writeAttribute("SeriesStart", AString_number_fixed(mult * m_start, 7));
300 xml.writeAttribute("SeriesStep", AString_number_fixed(mult * m_step, 7));
301 xml.writeAttribute("SeriesUnit", unitString);
302 }
0 #ifndef __CIFTI_SERIES_MAP_H__
1 #define __CIFTI_SERIES_MAP_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiMappingType.h"
31
32 namespace cifti
33 {
34 class CiftiSeriesMap : public CiftiMappingType
35 {
36 public:
37 enum Unit
38 {
39 HERTZ,
40 METER,
41 RADIAN,
42 SECOND
43 };//should this go somewhere else?
44 float getStart() const { return m_start; }//using getter/setter as style choice to match other mapping types
45 float getStep() const { return m_step; }//getter for number of series points is getLength(), specified by CiftiIndexMap
46 Unit getUnit() const { return m_unit; }
47
48 CiftiSeriesMap()
49 {
50 m_start = 0.0f;
51 m_step = 1.0f;
52 m_unit = SECOND;
53 m_length = -1;//to make it clear an improperly initialized series map object was used
54 }
55 CiftiSeriesMap(const int64_t& length, const float& start = 0.0f, const float& step = 1.0f, const Unit& unit = SECOND)
56 {
57 m_start = start;
58 m_step = step;
59 m_unit = unit;
60 m_length = length;
61 }
62 void setStart(const float& start) { m_start = start; }
63 void setStep(const float& step) { m_step = step; }
64 void setUnit(const Unit& unit) { m_unit = unit; }
65 void setLength(const int64_t& length);
66
67 static Unit stringToUnit(const AString& string, bool& ok);
68 static AString unitToString(const Unit& theUnit);
69 static std::vector<Unit> getAllUnits();
70
71 CiftiMappingType* clone() const { return new CiftiSeriesMap(*this); }
72 MappingType getType() const { return SERIES; }
73 int64_t getLength() const { return m_length; }
74 bool operator==(const CiftiMappingType& rhs) const
75 {
76 if (rhs.getType() != getType()) return false;
77 const CiftiSeriesMap& temp = dynamic_cast<const CiftiSeriesMap&>(rhs);
78 return (temp.m_length == m_length &&
79 temp.m_unit == m_unit &&
80 temp.m_start == m_start &&
81 temp.m_step == m_step);
82 }
83 bool approximateMatch(const CiftiMappingType& rhs, AString* explanation = NULL) const
84 {
85 switch (rhs.getType())
86 {
87 case SCALARS://maybe?
88 case SERIES:
89 case LABELS://maybe?
90 if (getLength() != rhs.getLength())
91 {
92 if (explanation != NULL) *explanation = "mappings have different length";
93 return false;
94 } else return true;
95 default:
96 if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType());
97 return false;
98 }
99 }
100 void readXML1(XmlReader& xml);
101 void readXML2(XmlReader& xml);
102 void writeXML1(XmlWriter& xml) const;
103 void writeXML2(XmlWriter& xml) const;
104 private:
105 int64_t m_length;
106 float m_start, m_step;//exponent gets applied to these on reading
107 Unit m_unit;
108 };
109 }
110
111 #endif //__CIFTI_SERIES_MAP_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiVersion.h"
28
29 #include "Common/CiftiException.h"
30
31 using namespace std;
32 using namespace cifti;
33
34 CiftiVersion::CiftiVersion()
35 {
36 m_major = 2;
37 m_minor = 0;
38 }
39
40 CiftiVersion::CiftiVersion(const int16_t& major, const int16_t& minor)
41 {
42 m_major = major;
43 m_minor = minor;
44 }
45
46 CiftiVersion::CiftiVersion(const AString& versionString)
47 {
48 bool ok = false;
49 #ifdef CIFTILIB_USE_QT
50 int result = versionString.indexOf('.');
51 if (result < 0)
52 {
53 m_minor = 0;
54 m_major = versionString.toShort(&ok);
55 if (!ok) throw CiftiException("improperly formatted CIFTI version string: '" + versionString + "'");
56 } else {
57 if (result == 0) throw CiftiException("improperly formatted CIFTI version string: '" + versionString + "'");
58 m_major = versionString.mid(0, result).toShort(&ok);
59 if (!ok) throw CiftiException("improperly formatted CIFTI version string: '" + versionString + "'");
60 m_minor = versionString.mid(result + 1).toShort(&ok);
61 if (!ok) throw CiftiException("improperly formatted CIFTI version string: '" + versionString + "'");
62 }
63 #else
64 #ifdef CIFTILIB_USE_XMLPP
65 size_t result = versionString.find('.');
66 if (result == AString::npos)
67 {
68 m_minor = 0;
69 m_major = (int16_t)AString_toInt(versionString, ok);
70 if (!ok) throw CiftiException("improperly formatted version string: " + versionString);
71 } else {
72 if (result == 0) throw CiftiException("improperly formatted version string: " + versionString);
73 m_major = (int16_t)AString_toInt(versionString.substr(0, result), ok);
74 if (!ok) throw CiftiException("improperly formatted version string: " + versionString);
75 m_minor = (int16_t)AString_toInt(versionString.substr(result + 1), ok);
76 if (!ok) throw CiftiException("improperly formatted version string: " + versionString);
77 }
78 #else
79 #error "not implemented"
80 #endif
81 #endif
82 }
83
84 bool CiftiVersion::hasReversedFirstDims() const
85 {
86 if (m_major == 1 && m_minor == 0) return true;
87 return false;
88 }
89
90 bool CiftiVersion::operator<(const CiftiVersion& rhs) const
91 {
92 if (m_major < rhs.m_major) return true;
93 if (m_major == rhs.m_major && m_minor < rhs.m_minor) return true;
94 return false;
95 }
96
97 bool CiftiVersion::operator<=(const CiftiVersion& rhs) const
98 {
99 if (m_major < rhs.m_major) return true;
100 if (m_major == rhs.m_major && m_minor <= rhs.m_minor) return true;
101 return false;
102 }
103
104 bool CiftiVersion::operator==(const CiftiVersion& rhs) const
105 {
106 if (m_major == rhs.m_major && m_minor == rhs.m_minor) return true;
107 return false;
108 }
109
110 bool CiftiVersion::operator!=(const CiftiVersion& rhs) const
111 {
112 return !(*this == rhs);
113 }
114
115 bool CiftiVersion::operator>(const cifti::CiftiVersion& rhs) const
116 {
117 if (m_major > rhs.m_major) return true;
118 if (m_major == rhs.m_major && m_minor > rhs.m_minor) return true;
119 return false;
120 }
121
122 bool CiftiVersion::operator>=(const cifti::CiftiVersion& rhs) const
123 {
124 if (m_major > rhs.m_major) return true;
125 if (m_major == rhs.m_major && m_minor >= rhs.m_minor) return true;
126 return false;
127 }
128
129 AString CiftiVersion::toString() const
130 {
131 AString ret = AString_number(m_major);
132 if (m_minor != 0) ret += "." + AString_number(m_minor);
133 return ret;
134 }
0 #ifndef __CIFTI_VERSION_H__
1 #define __CIFTI_VERSION_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/AString.h"
31
32 #include "stdint.h"
33
34 namespace cifti
35 {
36 class CiftiVersion
37 {
38 int16_t m_major, m_minor;
39 public:
40 int16_t getMajor() const { return m_major; }
41 int16_t getMinor() const { return m_minor; }
42
43 CiftiVersion();
44 CiftiVersion(const int16_t& major, const int16_t& minor);
45 CiftiVersion(const AString& versionString);
46 AString toString() const;
47 bool operator<(const CiftiVersion& rhs) const;
48 bool operator>(const CiftiVersion& rhs) const;
49 bool operator==(const CiftiVersion& rhs) const;
50 bool operator!=(const CiftiVersion& rhs) const;
51 bool operator<=(const CiftiVersion& rhs) const;
52 bool operator>=(const CiftiVersion& rhs) const;
53 ///quirk tests
54 bool hasReversedFirstDims() const;
55 };
56 }
57
58 #endif //__CIFTI_VERSION_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiXML.h"
28
29 #include "Common/CiftiException.h"
30 #include "Common/XmlAdapter.h"
31
32 #include <cassert>
33 #include <iostream>
34 #include <set>
35
36 using namespace std;
37 using namespace boost;
38 using namespace cifti;
39
40 CiftiXML::CiftiXML(const CiftiXML& rhs)
41 {
42 copyHelper(rhs);
43 }
44
45 CiftiXML& CiftiXML::operator=(const CiftiXML& rhs)
46 {
47 if (this != &rhs) copyHelper(rhs);
48 return *this;
49 }
50
51 void CiftiXML::copyHelper(const CiftiXML& rhs)
52 {
53 int numDims = (int)rhs.m_indexMaps.size();
54 m_indexMaps.resize(numDims);
55 for (int i = 0; i < numDims; ++i)
56 {
57 m_indexMaps[i] = boost::shared_ptr<CiftiMappingType>(rhs.m_indexMaps[i]->clone());
58 }
59 m_parsedVersion = rhs.m_parsedVersion;
60 m_fileMetaData = rhs.m_fileMetaData;
61 }
62
63 bool CiftiXML::operator==(const CiftiXML& rhs) const
64 {
65 int numDims = getNumberOfDimensions();
66 if (rhs.getNumberOfDimensions() != numDims) return false;
67 if (m_fileMetaData != rhs.m_fileMetaData) return false;
68 for (int i = 0; i < numDims; ++i)
69 {
70 const CiftiMappingType* left = getMap(i), *right = rhs.getMap(i);
71 if (left == NULL && right == NULL) continue;
72 if (left == NULL || right == NULL) return false;//only one NULL, due to above test
73 if ((*left) != (*right)) return false;//finally can dereference them
74 }
75 return true;
76 }
77
78 bool CiftiXML::approximateMatch(const CiftiXML& rhs) const
79 {
80 int numDims = getNumberOfDimensions();
81 if (rhs.getNumberOfDimensions() != numDims) return false;
82 for (int i = 0; i < numDims; ++i)
83 {
84 const CiftiMappingType* left = getMap(i), *right = rhs.getMap(i);
85 if (left == NULL && right == NULL) continue;
86 if (left == NULL || right == NULL) return false;//only one NULL, due to above test
87 if (!left->approximateMatch(*right)) return false;//finally can dereference them
88 }
89 return true;
90 }
91
92 const CiftiMappingType* CiftiXML::getMap(const int& direction) const
93 {
94 CiftiAssertVectorIndex(m_indexMaps, direction);
95 return m_indexMaps[direction].get();
96 }
97
98 CiftiMappingType* CiftiXML::getMap(const int& direction)
99 {
100 CiftiAssertVectorIndex(m_indexMaps, direction);
101 return m_indexMaps[direction].get();
102 }
103
104 const MetaData& CiftiXML::getFileMetaData() const
105 {
106 return m_fileMetaData;
107 }
108
109 const CiftiBrainModelsMap& CiftiXML::getBrainModelsMap(const int& direction) const
110 {
111 CiftiAssertVectorIndex(m_indexMaps, direction);
112 CiftiAssert(getMappingType(direction) == CiftiMappingType::BRAIN_MODELS);//assert so we catch it in debug
113 return dynamic_cast<const CiftiBrainModelsMap&>(*getMap(direction));//let release throw bad_cast or segfault
114 }
115
116 CiftiBrainModelsMap& CiftiXML::getBrainModelsMap(const int& direction)
117 {
118 CiftiAssertVectorIndex(m_indexMaps, direction);
119 CiftiAssert(getMappingType(direction) == CiftiMappingType::BRAIN_MODELS);//assert so we catch it in debug
120 return dynamic_cast<CiftiBrainModelsMap&>(*getMap(direction));//let release throw bad_cast or segfault
121 }
122
123 const CiftiLabelsMap& CiftiXML::getLabelsMap(const int& direction) const
124 {
125 CiftiAssertVectorIndex(m_indexMaps, direction);
126 CiftiAssert(getMappingType(direction) == CiftiMappingType::LABELS);//assert so we catch it in debug
127 return dynamic_cast<const CiftiLabelsMap&>(*getMap(direction));//let release throw bad_cast or segfault
128 }
129
130 CiftiLabelsMap& CiftiXML::getLabelsMap(const int& direction)
131 {
132 CiftiAssertVectorIndex(m_indexMaps, direction);
133 CiftiAssert(getMappingType(direction) == CiftiMappingType::LABELS);//assert so we catch it in debug
134 return dynamic_cast<CiftiLabelsMap&>(*getMap(direction));//let release throw bad_cast or segfault
135 }
136
137 const CiftiParcelsMap& CiftiXML::getParcelsMap(const int& direction) const
138 {
139 CiftiAssertVectorIndex(m_indexMaps, direction);
140 CiftiAssert(getMappingType(direction) == CiftiMappingType::PARCELS);//assert so we catch it in debug
141 return dynamic_cast<const CiftiParcelsMap&>(*getMap(direction));//let release throw bad_cast or segfault
142 }
143
144 CiftiParcelsMap& CiftiXML::getParcelsMap(const int& direction)
145 {
146 CiftiAssertVectorIndex(m_indexMaps, direction);
147 CiftiAssert(getMappingType(direction) == CiftiMappingType::PARCELS);//assert so we catch it in debug
148 return dynamic_cast<CiftiParcelsMap&>(*getMap(direction));//let release throw bad_cast or segfault
149 }
150
151 const CiftiScalarsMap& CiftiXML::getScalarsMap(const int& direction) const
152 {
153 CiftiAssertVectorIndex(m_indexMaps, direction);
154 CiftiAssert(getMappingType(direction) == CiftiMappingType::SCALARS);//assert so we catch it in debug
155 return dynamic_cast<const CiftiScalarsMap&>(*getMap(direction));//let release throw bad_cast or segfault
156 }
157
158 CiftiScalarsMap& CiftiXML::getScalarsMap(const int& direction)
159 {
160 CiftiAssertVectorIndex(m_indexMaps, direction);
161 CiftiAssert(getMappingType(direction) == CiftiMappingType::SCALARS);//assert so we catch it in debug
162 return dynamic_cast<CiftiScalarsMap&>(*getMap(direction));//let release throw bad_cast or segfault
163 }
164
165 const CiftiSeriesMap& CiftiXML::getSeriesMap(const int& direction) const
166 {
167 CiftiAssertVectorIndex(m_indexMaps, direction);
168 CiftiAssert(getMappingType(direction) == CiftiMappingType::SERIES);//assert so we catch it in debug
169 return dynamic_cast<const CiftiSeriesMap&>(*getMap(direction));//let release throw bad_cast or segfault
170 }
171
172 CiftiSeriesMap& CiftiXML::getSeriesMap(const int& direction)
173 {
174 CiftiAssertVectorIndex(m_indexMaps, direction);
175 CiftiAssert(getMappingType(direction) == CiftiMappingType::SERIES);//assert so we catch it in debug
176 return dynamic_cast<CiftiSeriesMap&>(*getMap(direction));//let release throw bad_cast or segfault
177 }
178
179 int64_t CiftiXML::getDimensionLength(const int& direction) const
180 {
181 const CiftiMappingType* tempMap = getMap(direction);
182 CiftiAssert(tempMap != NULL);
183 return tempMap->getLength();
184 }
185
186 vector<int64_t> CiftiXML::getDimensions() const
187 {
188 vector<int64_t> ret(getNumberOfDimensions());
189 for (int i = 0; i < (int)ret.size(); ++i)
190 {
191 ret[i] = getDimensionLength(i);
192 }
193 return ret;
194 }
195
196 CiftiMappingType::MappingType CiftiXML::getMappingType(const int& direction) const
197 {
198 CiftiAssertVectorIndex(m_indexMaps, direction);
199 CiftiAssert(m_indexMaps[direction] != NULL);
200 return m_indexMaps[direction]->getType();
201 }
202
203 void CiftiXML::setMap(const int& direction, const CiftiMappingType& mapIn)
204 {
205 CiftiAssertVectorIndex(m_indexMaps, direction);
206 if (mapIn.getType() == CiftiMappingType::LABELS)
207 {
208 for (int i = 0; i < getNumberOfDimensions(); ++i)
209 {
210 if (i != direction && m_indexMaps[i] != NULL && m_indexMaps[i]->getType() == CiftiMappingType::LABELS)
211 {
212 throw CiftiException("Cifti XML cannot contain more than one label mapping");
213 }
214 }
215 }
216 m_indexMaps[direction] = boost::shared_ptr<CiftiMappingType>(mapIn.clone());
217 }
218
219 void CiftiXML::setNumberOfDimensions(const int& num)
220 {
221 m_indexMaps.resize(num);
222 }
223
224 void CiftiXML::clear()
225 {
226 setNumberOfDimensions(0);
227 m_fileMetaData.clear();
228 m_parsedVersion = CiftiVersion();
229 }
230
231 void CiftiXML::readXML(const vector<char>& text)
232 {
233 #ifdef CIFTILIB_USE_QT
234 vector<char> text2 = text;
235 text2.push_back('\0');//make sure it has a null terminator
236 XmlReader xml(text2.data());//so it works as a C string
237 #else
238 #ifdef CIFTILIB_USE_XMLPP
239 vector<char>::const_iterator end = find(text.begin(), text.end(), '\0');//find the null terminator, if it exists, to prevent "extra content at end of document" errors
240 XmlReader xml((unsigned char*)text.data(), end - text.begin());//get the number of bytes
241 #else
242 #error "not implemented"
243 #endif
244 #endif
245 readXML(xml);
246 }
247
248 int32_t CiftiXML::getIntentInfo(const CiftiVersion& writingVersion, char intentNameOut[16]) const
249 {
250 int32_t ret;
251 const char* name = NULL;
252 if (writingVersion == CiftiVersion(1, 0))//cifti-1: unknown didn't exist, and "ConnDense" was default in workbench
253 {
254 ret = 3001;//default
255 name = "ConnDense";
256 if (getNumberOfDimensions() > 0 && getMappingType(0) == CiftiMappingType::SERIES) { ret = 3002; name = "ConnDenseTime"; }//same logic as was used in workbench
257 if (getNumberOfDimensions() > 1 && getMappingType(1) == CiftiMappingType::SERIES) { ret = 3002; name = "ConnDenseTime"; }//NOTE: name for this code is different than cifti-2
258 } else if (writingVersion == CiftiVersion(1, 1) || writingVersion == CiftiVersion(2, 0)) {//cifti-2
259 ret = 3000;//default
260 name = "ConnUnknown";
261 switch (getNumberOfDimensions())
262 {
263 case 2:
264 {
265 CiftiMappingType::MappingType first = getMappingType(0), second = getMappingType(1);
266 if (first == CiftiMappingType::BRAIN_MODELS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3001; name = "ConnDense"; }
267 if (first == CiftiMappingType::SERIES && second == CiftiMappingType::BRAIN_MODELS) { ret = 3002; name = "ConnDenseSeries"; }
268 if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::PARCELS) { ret = 3003; name = "ConnParcels"; }
269 if (first == CiftiMappingType::SERIES && second == CiftiMappingType::PARCELS) { ret = 3004; name = "ConnParcelSries"; }//NOTE: 3005 is reserved but not used
270 if (first == CiftiMappingType::SCALARS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3006; name = "ConnDenseScalar"; }
271 if (first == CiftiMappingType::LABELS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3007; name = "ConnDenseLabel"; }
272 if (first == CiftiMappingType::SCALARS && second == CiftiMappingType::PARCELS) { ret = 3008; name = "ConnParcelScalr"; }
273 if (first == CiftiMappingType::BRAIN_MODELS && second == CiftiMappingType::PARCELS) { ret = 3009; name = "ConnParcelDense"; }
274 if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3010; name = "ConnDenseParcel"; }
275 break;
276 }
277 case 3:
278 {
279 CiftiMappingType::MappingType first = getMappingType(0), second = getMappingType(1), third = getMappingType(2);
280 if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::PARCELS && third == CiftiMappingType::SERIES) { ret = 3011; name = "ConnPPSr"; }
281 if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::PARCELS && third == CiftiMappingType::SCALARS) { ret = 3012; name = "ConnPPSc"; }
282 break;
283 }
284 default:
285 break;
286 }
287 } else {
288 throw CiftiException("unknown cifti version: " + writingVersion.toString());
289 }
290 int i;
291 for (i = 0; i < 16 && name[i] != '\0'; ++i) intentNameOut[i] = name[i];
292 for (; i < 16; ++i) intentNameOut[i] = '\0';
293 return ret;
294 }
295
296 void CiftiXML::readXML(XmlReader& xml)
297 {
298 clear();
299 bool haveCifti = false;
300 try
301 {
302 #ifdef CIFTILIB_USE_QT
303 for (; !xml.atEnd(); xml.readNext())
304 {
305 if (xml.isStartElement())
306 {
307 QStringRef name = xml.name();
308 if (name == "CIFTI")
309 {
310 if (haveCifti)
311 {
312 throw CiftiException("CIFTI element may only be specified once");
313 }
314 QXmlStreamAttributes attributes = xml.attributes();
315 if(attributes.hasAttribute("Version"))
316 {
317 m_parsedVersion = CiftiVersion(attributes.value("Version").toString());
318 } else {
319 throw CiftiException("Cifti XML missing Version attribute.");
320 }
321 if (m_parsedVersion == CiftiVersion(1, 0))//switch/case on major/minor would be much harder to read
322 {
323 parseCIFTI1(xml);
324 if (xml.hasError()) break;
325 } else if (m_parsedVersion == CiftiVersion(2, 0)) {
326 parseCIFTI2(xml);
327 if (xml.hasError()) break;
328 } else {
329 throw CiftiException("unknown Cifti Version: '" + m_parsedVersion.toString());
330 }
331 haveCifti = true;
332 } else {
333 throw CiftiException("unexpected root element in Cifti XML: " + name.toString());
334 }
335 }
336 }
337 if (!xml.hasError() && !haveCifti)
338 {
339 throw CiftiException("CIFTI element not found");
340 }
341 #else
342 #ifdef CIFTILIB_USE_XMLPP
343 while (true)//don't want to skip the first element, but there is no obvious function for "at end"
344 {
345 switch (xml.get_node_type())
346 {
347 case XmlReader::Element:
348 {
349 AString name = xml.get_local_name();
350 if (name == "CIFTI")
351 {
352 if (haveCifti)
353 {
354 throw CiftiException("CIFTI element may only be specified once");
355 }
356 vector<AString> mandAttrs(1, "Version");
357 XmlAttributesResult attrVals = XmlReader_parseAttributes(xml, mandAttrs);
358 m_parsedVersion = CiftiVersion(attrVals.mandatoryVals[0]);
359 if (m_parsedVersion == CiftiVersion(1, 0))//switch/case on major/minor would be much harder to read
360 {
361 parseCIFTI1(xml);
362 } else if (m_parsedVersion == CiftiVersion(2, 0)) {
363 parseCIFTI2(xml);
364 } else {
365 throw CiftiException("unknown Cifti Version: '" + m_parsedVersion.toString());
366 }
367 haveCifti = true;
368 } else {
369 throw CiftiException("unexpected root element in Cifti XML: " + name);
370 }
371 }
372 default:
373 break;
374 }
375 if (!xml.read()) break;
376 }
377 #else
378 #error "not implemented"
379 #endif
380 #endif
381 } catch (CiftiException& e) {
382 throw CiftiException("Cifti XML error: " + e.whatString());//so we can throw on error instead of doing a bunch of dancing with xml.raiseError and xml.hasError
383 } catch (std::exception& e) {//libxml++ throws things that inherit from std::exception, catch them too
384 throw CiftiException("Cifti XML error: " + AString(e.what()));
385 }
386 #ifdef CIFTILIB_USE_QT
387 if(xml.hasError())
388 {
389 throw CiftiException("Cifti XML error: " + xml.errorString());
390 }
391 #endif
392 }
393
394 void CiftiXML::parseCIFTI1(XmlReader& xml)
395 {
396 bool haveMatrix = false;
397 #ifdef CIFTILIB_USE_QT
398 QXmlStreamAttributes attributes = xml.attributes();
399 if (attributes.hasAttribute("NumberOfMatrices"))
400 {
401 if (attributes.value("NumberOfMatrices") != "1")
402 {
403 throw CiftiException("attribute NumberOfMatrices in CIFTI is required to be 1 for CIFTI-1");
404 }
405 } else {
406 throw CiftiException("missing attribute NumberOfMatrices in CIFTI");
407 }
408 while (!xml.atEnd())
409 {
410 xml.readNext();
411 if (xml.isStartElement())
412 {
413 QStringRef name = xml.name();
414 if (name == "Matrix")
415 {
416 if (haveMatrix)
417 {
418 throw CiftiException("Matrix element may only be specified once");
419 }
420 parseMatrix1(xml);
421 if (xml.hasError()) return;
422 haveMatrix = true;
423 } else {
424 throw CiftiException("unexpected element in CIFTI: " + name.toString());
425 }
426 } else if (xml.isEndElement()) {
427 break;
428 }
429 }
430 if (xml.hasError()) return;
431 #else
432 #ifdef CIFTILIB_USE_XMLPP
433 vector<AString> mandAttrs(1, "NumberOfMatrices");
434 XmlAttributesResult attrVals = XmlReader_parseAttributes(xml, mandAttrs);
435 if (attrVals.mandatoryVals[0] != "1")
436 {
437 throw CiftiException("attribute NumberOfMatrices in CIFTI is required to be 1 for CIFTI-1");
438 }
439 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
440 while(!done && xml.read())
441 {
442 switch (xml.get_node_type())
443 {
444 case XmlReader::Element:
445 {
446 AString name = xml.get_local_name();
447 if (name == "Matrix")
448 {
449 if (haveMatrix)
450 {
451 throw CiftiException("Matrix element may only be specified once");
452 }
453 parseMatrix1(xml);
454 haveMatrix = true;
455 } else {
456 throw CiftiException("unexpected element in CIFTI: " + name);
457 }
458 break;
459 }
460 case XmlReader::EndElement:
461 done = true;
462 break;
463 default:
464 break;
465 }
466 }
467 #else
468 #error "not implemented"
469 #endif
470 #endif
471 CiftiAssert(XmlReader_checkEndElement(xml, "CIFTI"));
472 if (!haveMatrix)
473 {
474 throw CiftiException("Matrix element not found in CIFTI");
475 }
476 }
477
478 void CiftiXML::parseCIFTI2(XmlReader& xml)//yes, these will often have largely similar code, but it seems cleaner than having only some functions split, or constantly rechecking the version
479 {//also, helps keep changes to cifti-2 away from code that parses cifti-1
480 bool haveMatrix = false;
481 #ifdef CIFTILIB_USE_QT
482 while (!xml.atEnd())
483 {
484 xml.readNext();
485 if (xml.hasError()) return;
486 if (xml.isStartElement())
487 {
488 QStringRef name = xml.name();
489 if (name == "Matrix")
490 {
491 if (haveMatrix)
492 {
493 throw CiftiException("Matrix element may only be specified once");
494 }
495 parseMatrix2(xml);
496 if (xml.hasError()) return;
497 haveMatrix = true;
498 } else {
499 throw CiftiException("unexpected element in CIFTI: " + name.toString());
500 }
501 } else if (xml.isEndElement()) {
502 break;
503 }
504 }
505 #else
506 #ifdef CIFTILIB_USE_XMLPP
507 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
508 while(!done && xml.read())
509 {
510 switch (xml.get_node_type())
511 {
512 case XmlReader::Element:
513 {
514 AString name = xml.get_local_name();
515 if (name == "Matrix")
516 {
517 if (haveMatrix)
518 {
519 throw CiftiException("Matrix element may only be specified once");
520 }
521 parseMatrix2(xml);
522 haveMatrix = true;
523 } else {
524 throw CiftiException("unexpected element in CIFTI: " + name);
525 }
526 break;
527 }
528 case XmlReader::EndElement:
529 done = true;
530 break;
531 default:
532 break;
533 }
534 }
535 #else
536 #error "not implemented"
537 #endif
538 #endif
539 CiftiAssert(XmlReader_checkEndElement(xml, "CIFTI"));
540 if (!haveMatrix)
541 {
542 throw CiftiException("Matrix element not found in CIFTI");
543 }
544 }
545
546 void CiftiXML::parseMatrix1(XmlReader& xml)
547 {
548 VolumeSpace fileVolSpace;
549 bool haveVolSpace = false, haveMetadata = false;
550 #ifdef CIFTILIB_USE_QT
551 while (!xml.atEnd())
552 {
553 xml.readNext();
554 if (xml.hasError()) return;
555 if (xml.isStartElement())
556 {
557 QStringRef name = xml.name();
558 if (name == "MetaData")
559 {
560 if (haveMetadata)
561 {
562 throw CiftiException("MetaData may only be specified once in Matrix");
563 }
564 m_fileMetaData.readCiftiXML1(xml);
565 if (xml.hasError()) return;
566 haveMetadata = true;
567 } else if (name == "MatrixIndicesMap") {
568 parseMatrixIndicesMap1(xml);
569 if (xml.hasError()) return;
570 } else if (name == "Volume") {
571 if (haveVolSpace)
572 {
573 throw CiftiException("Volume may only be specified once in Matrix");
574 }
575 fileVolSpace.readCiftiXML1(xml);
576 if (xml.hasError()) return;
577 haveVolSpace = true;
578 } else if (name == "LabelTable") {
579 xml.readElementText(XmlReader::SkipChildElements);
580 } else {
581 throw CiftiException("unexpected element in Matrix: " + name.toString());
582 }
583 } else if (xml.isEndElement()) {
584 break;
585 }
586 }
587 #else
588 #ifdef CIFTILIB_USE_XMLPP
589 bool skipread = false, done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
590 while(!done)
591 {
592 if (!skipread)
593 {
594 if (!xml.read()) break;
595 } else {
596 skipread = false;
597 }
598 switch (xml.get_node_type())
599 {
600 case XmlReader::Element:
601 {
602 AString name = xml.get_local_name();
603 if (name == "MetaData")
604 {
605 if (haveMetadata)
606 {
607 throw CiftiException("MetaData may only be specified once in Matrix");
608 }
609 m_fileMetaData.readCiftiXML1(xml);
610 haveMetadata = true;
611 } else if (name == "MatrixIndicesMap") {
612 parseMatrixIndicesMap1(xml);
613 } else if (name == "Volume") {
614 if (haveVolSpace)
615 {
616 throw CiftiException("Volume may only be specified once in Matrix");
617 }
618 fileVolSpace.readCiftiXML1(xml);
619 haveVolSpace = true;
620 } else if (name == "LabelTable") {
621 xml.next();//TODO: test if this does this actually does what we want
622 skipread = true;
623 } else {
624 throw CiftiException("unexpected element in Matrix: " + name);
625 }
626 break;
627 }
628 case XmlReader::EndElement:
629 done = true;
630 break;
631 default:
632 break;
633 }
634 }
635 #else
636 #error "not implemented"
637 #endif
638 #endif
639 CiftiAssert(XmlReader_checkEndElement(xml, "Matrix"));
640 for (int i = 0; i < (int)m_indexMaps.size(); ++i)
641 {
642 if (m_indexMaps[i] == NULL)
643 {
644 int displaynum = i;
645 if (displaynum < 2) displaynum = 1 - displaynum;//re-invert so that it shows the same number as the XML is missing
646 throw CiftiException("missing mapping for dimension '" + AString_number(displaynum) + "'");
647 }
648 switch (m_indexMaps[i]->getType())
649 {
650 case CiftiMappingType::BRAIN_MODELS:
651 {
652 CiftiBrainModelsMap& myMap = dynamic_cast<CiftiBrainModelsMap&>(*(m_indexMaps[i]));
653 if (myMap.hasVolumeData())
654 {
655 if (haveVolSpace)
656 {
657 myMap.setVolumeSpace(fileVolSpace);//also does the needed checking of voxel indices
658 } else {
659 throw CiftiException("BrainModels map uses voxels, but no Volume element exists");
660 }
661 }
662 break;
663 }
664 case CiftiMappingType::PARCELS:
665 {
666 CiftiParcelsMap& myMap = dynamic_cast<CiftiParcelsMap&>(*(m_indexMaps[i]));
667 if (myMap.hasVolumeData())
668 {
669 if (haveVolSpace)
670 {
671 myMap.setVolumeSpace(fileVolSpace);//ditto
672 } else {
673 throw CiftiException("Parcels map uses voxels, but no Volume element exists");
674 }
675 }
676 break;
677 }
678 default:
679 break;
680 }
681 }
682 }
683
684 void CiftiXML::parseMatrix2(XmlReader& xml)
685 {
686 bool haveMetadata = false;
687 #ifdef CIFTILIB_USE_QT
688 while (!xml.atEnd())
689 {
690 xml.readNext();
691 if (xml.hasError()) return;
692 if (xml.isStartElement())
693 {
694 QStringRef name = xml.name();
695 if (name == "MetaData")
696 {
697 if (haveMetadata)
698 {
699 throw CiftiException("MetaData may only be specified once in Matrix");
700 }
701 m_fileMetaData.readCiftiXML2(xml);
702 if (xml.hasError()) return;
703 haveMetadata = true;
704 } else if (name == "MatrixIndicesMap") {
705 parseMatrixIndicesMap2(xml);
706 if (xml.hasError()) return;
707 } else {
708 throw CiftiException("unexpected element in Matrix: " + name.toString());
709 }
710 } else if (xml.isEndElement()) {
711 break;
712 }
713 }
714 #else
715 #ifdef CIFTILIB_USE_XMLPP
716 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
717 while(!done && xml.read())
718 {
719 switch (xml.get_node_type())
720 {
721 case XmlReader::Element:
722 {
723 AString name = xml.get_local_name();
724 if (name == "MetaData")
725 {
726 if (haveMetadata)
727 {
728 throw CiftiException("MetaData may only be specified once in Matrix");
729 }
730 m_fileMetaData.readCiftiXML2(xml);
731 haveMetadata = true;
732 } else if (name == "MatrixIndicesMap") {
733 parseMatrixIndicesMap2(xml);
734 } else {
735 throw CiftiException("unexpected element in Matrix: " + name);
736 }
737 break;
738 }
739 case XmlReader::EndElement:
740 done = true;
741 break;
742 default:
743 break;
744 }
745 }
746 #else
747 #error "not implemented"
748 #endif
749 #endif
750 CiftiAssert(XmlReader_checkEndElement(xml, "Matrix"));
751 for (int i = 0; i < (int)m_indexMaps.size(); ++i)
752 {
753 if (m_indexMaps[i] == NULL)
754 {
755 throw CiftiException("missing mapping for dimension '" + AString_number(i) + "'");
756 }
757 }
758 }
759
760 void CiftiXML::parseMatrixIndicesMap1(XmlReader& xml)
761 {
762 vector<AString> mandAttrs(2);
763 mandAttrs[0] = "AppliesToMatrixDimension";
764 mandAttrs[1] = "IndicesMapToDataType";
765 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
766 vector<AString> values = AString_split(myAttrs.mandatoryVals[0], ',');
767 bool ok = false;
768 set<int> used;
769 for(int i = 0; i < (int)values.size(); i++)
770 {
771 int parsed = AString_toInt(values[i], ok);
772 if (!ok || parsed < 0)
773 {
774 throw CiftiException("bad value in AppliesToMatrixDimension list: " + values[i]);
775 }
776 if (parsed < 2) parsed = 1 - parsed;//in other words, 0 becomes 1 and 1 becomes 0, since cifti-1 had them reversed
777 if (used.find(parsed) != used.end())
778 {
779 throw CiftiException("AppliesToMatrixDimension contains repeated value: " + values[i]);
780 }
781 used.insert(parsed);
782 }
783 boost::shared_ptr<CiftiMappingType> toRead;
784 AString type = myAttrs.mandatoryVals[1];
785 if (type == "CIFTI_INDEX_TYPE_BRAIN_MODELS")
786 {
787 toRead = boost::shared_ptr<CiftiBrainModelsMap>(new CiftiBrainModelsMap());
788 } else if (type == "CIFTI_INDEX_TYPE_TIME_POINTS") {
789 toRead = boost::shared_ptr<CiftiSeriesMap>(new CiftiSeriesMap());
790 } else if (type == "CIFTI_INDEX_TYPE_LABELS") {//this and below are nonstandard
791 toRead = boost::shared_ptr<CiftiLabelsMap>(new CiftiLabelsMap());
792 } else if (type == "CIFTI_INDEX_TYPE_PARCELS") {
793 toRead = boost::shared_ptr<CiftiParcelsMap>(new CiftiParcelsMap());
794 } else if (type == "CIFTI_INDEX_TYPE_SCALARS") {
795 toRead = boost::shared_ptr<CiftiScalarsMap>(new CiftiScalarsMap());
796 } else {
797 throw CiftiException("invalid value for IndicesMapToDataType in CIFTI-1: " + type);
798 }
799 toRead->readXML1(xml);//this will warn if it is nonstandard
800 #ifdef CIFTILIB_USE_QT
801 if (xml.hasError()) return;
802 #endif
803 if (toRead->getLength() < 1 && !(type == "CIFTI_INDEX_TYPE_TIME_POINTS" && toRead->getLength() == -1)) throw CiftiException("cifti mapping type with zero length found");
804 bool first = true;//NOTE: series maps didn't encode length in cifti-1, -1 is used as a stand-in, CiftiFile fills in the length from the matrix
805 for (set<int>::iterator iter = used.begin(); iter != used.end(); ++iter)
806 {
807 if (*iter >= (int)m_indexMaps.size()) m_indexMaps.resize(*iter + 1);
808 if (first)
809 {
810 m_indexMaps[*iter] = toRead;
811 first = false;
812 } else {
813 m_indexMaps[*iter] = boost::shared_ptr<CiftiMappingType>(toRead->clone());//make in-memory information independent per-dimension, rather than dealing with deduplication everywhere
814 }
815 }
816 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
817 }
818
819 void CiftiXML::parseMatrixIndicesMap2(XmlReader& xml)
820 {
821 vector<AString> mandAttrs(2);
822 mandAttrs[0] = "AppliesToMatrixDimension";
823 mandAttrs[1] = "IndicesMapToDataType";
824 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
825 vector<AString> values = AString_split(myAttrs.mandatoryVals[0], ',');
826 bool ok = false;
827 set<int> used;
828 for(int i = 0; i < (int)values.size(); i++)
829 {
830 int parsed = AString_toInt(values[i], ok);
831 if (!ok || parsed < 0)
832 {
833 throw CiftiException("bad value in AppliesToMatrixDimension list: " + values[i]);
834 }
835 if (used.find(parsed) != used.end())
836 {
837 throw CiftiException("AppliesToMatrixDimension contains repeated value: " + values[i]);
838 }
839 used.insert(parsed);
840 }
841 boost::shared_ptr<CiftiMappingType> toRead;
842 AString type = myAttrs.mandatoryVals[1];
843 if (type == "CIFTI_INDEX_TYPE_BRAIN_MODELS")
844 {
845 toRead = boost::shared_ptr<CiftiBrainModelsMap>(new CiftiBrainModelsMap());
846 } else if (type == "CIFTI_INDEX_TYPE_LABELS") {
847 toRead = boost::shared_ptr<CiftiLabelsMap>(new CiftiLabelsMap());
848 } else if (type == "CIFTI_INDEX_TYPE_PARCELS") {
849 toRead = boost::shared_ptr<CiftiParcelsMap>(new CiftiParcelsMap());
850 } else if (type == "CIFTI_INDEX_TYPE_SCALARS") {
851 toRead = boost::shared_ptr<CiftiScalarsMap>(new CiftiScalarsMap());
852 } else if (type == "CIFTI_INDEX_TYPE_SERIES") {
853 toRead = boost::shared_ptr<CiftiSeriesMap>(new CiftiSeriesMap());
854 } else {
855 throw CiftiException("invalid value for IndicesMapToDataType in CIFTI-1: " + type);
856 }
857 toRead->readXML2(xml);
858 #ifdef CIFTILIB_USE_QT
859 if (xml.hasError()) return;
860 #endif
861 if (toRead->getLength() < 1) throw CiftiException("cifti mapping type with zero length found");
862 bool first = true;
863 for (set<int>::iterator iter = used.begin(); iter != used.end(); ++iter)
864 {
865 if (*iter >= (int)m_indexMaps.size()) m_indexMaps.resize(*iter + 1);
866 if (first)
867 {
868 m_indexMaps[*iter] = toRead;
869 first = false;
870 } else {
871 m_indexMaps[*iter] = boost::shared_ptr<CiftiMappingType>(toRead->clone());//make in-memory information independent per-dimension, rather than dealing with deduplication everywhere
872 }
873 }
874 CiftiAssert(XmlReader_checkEndElement(xml, "MatrixIndicesMap"));
875 }
876
877 vector<char> CiftiXML::writeXMLToVector(const CiftiVersion& writingVersion) const
878 {
879 #ifdef CIFTILIB_USE_QT
880 QByteArray tempArray;
881 XmlWriter xml(&tempArray);
882 xml.setAutoFormatting(true);
883 xml.writeStartDocument();
884 writeXML(xml, writingVersion);
885 xml.writeEndDocument();
886 int numBytes = tempArray.size();//QByteArray is limited to 2GB
887 vector<char> ret(numBytes + 1);//include room for null terminator
888 for (int i = 0; i < numBytes; ++i)
889 {
890 ret[i] = tempArray[i];
891 }
892 ret[numBytes] = '\0';
893 return ret;
894 #else
895 #ifdef CIFTILIB_USE_XMLPP
896 XmlWriter xml;
897 xml.writeStartDocument();
898 writeXML(xml, writingVersion);
899 xml.writeEndDocument();
900 return xml.getXmlData();
901 #else
902 #error "not implemented"
903 #endif
904 #endif
905 }
906
907 void CiftiXML::writeXML(XmlWriter& xml, const CiftiVersion& writingVersion) const
908 {
909 xml.writeStartElement("CIFTI");
910 xml.writeAttribute("Version", writingVersion.toString());
911 if (writingVersion == CiftiVersion(1, 0))//switch/case on major/minor would be much harder to read
912 {
913 xml.writeAttribute("NumberOfMatrices", "1");
914 writeMatrix1(xml);
915 } else if (writingVersion == CiftiVersion(2, 0)) {
916 writeMatrix2(xml);
917 } else {
918 throw CiftiException("unknown Cifti writing version: '" + writingVersion.toString() + "'");
919 }
920 xml.writeEndElement();
921 }
922
923 void CiftiXML::writeMatrix1(XmlWriter& xml) const
924 {
925 int numDims = (int)m_indexMaps.size();
926 bool haveVolData = false;
927 VolumeSpace volSpace;
928 for (int i = 0; i < numDims; ++i)
929 {
930 if (m_indexMaps[i] == NULL) throw CiftiException("dimension " + AString_number(i) + " was not given a mapping");
931 switch (m_indexMaps[i]->getType())
932 {
933 case CiftiMappingType::BRAIN_MODELS:
934 {
935 const CiftiBrainModelsMap& myMap = dynamic_cast<const CiftiBrainModelsMap&>(*(m_indexMaps[i]));
936 if (myMap.hasVolumeData())
937 {
938 if (haveVolData)
939 {
940 if (myMap.getVolumeSpace() != volSpace)
941 {
942 throw CiftiException("cannot write different volume spaces for different dimensions in CIFTI-1");
943 }
944 } else {
945 haveVolData = true;
946 volSpace = myMap.getVolumeSpace();
947 }
948 }
949 break;
950 }
951 case CiftiMappingType::PARCELS:
952 {
953 const CiftiParcelsMap& myMap = dynamic_cast<const CiftiParcelsMap&>(*(m_indexMaps[i]));
954 if (myMap.hasVolumeData())
955 {
956 if (haveVolData)
957 {
958 if (myMap.getVolumeSpace() != volSpace)
959 {
960 throw CiftiException("cannot write different volume spaces for different dimensions in CIFTI-1");
961 }
962 } else {
963 haveVolData = true;
964 volSpace = myMap.getVolumeSpace();
965 }
966 }
967 break;
968 }
969 default:
970 break;
971 }
972 }
973 xml.writeStartElement("Matrix");
974 m_fileMetaData.writeCiftiXML1(xml);
975 if (haveVolData)
976 {
977 volSpace.writeCiftiXML1(xml);
978 }
979 vector<bool> used(numDims, false);
980 for (int i = 0; i < numDims; ++i)
981 {
982 if (!used[i])
983 {
984 used[i] = true;
985 int outputNum = i;
986 if (outputNum < 2) outputNum = 1 - outputNum;//ie, swap 0 and 1
987 AString appliesTo = AString_number(outputNum);//initialize containing just the current dimension
988 for (int j = i + 1; j < numDims; ++j)//compare to all later unused dimensions for deduplication
989 {//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
990 if (!used[j])
991 {
992 if ((*m_indexMaps[i]) == (*m_indexMaps[j]))
993 {
994 outputNum = j;
995 if (outputNum < 2) outputNum = 1 - outputNum;
996 appliesTo += "," + AString_number(outputNum);
997 used[j] = true;
998 }
999 }
1000 }
1001 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
1002 xml.writeAttribute("AppliesToMatrixDimension", appliesTo);
1003 m_indexMaps[i]->writeXML1(xml);
1004 xml.writeEndElement();
1005 }
1006 }
1007 xml.writeEndElement();
1008 }
1009
1010 void CiftiXML::writeMatrix2(XmlWriter& xml) const
1011 {
1012 int numDims = (int)m_indexMaps.size();
1013 for (int i = 0; i < numDims; ++i)
1014 {
1015 if (m_indexMaps[i] == NULL) throw CiftiException("dimension " + AString_number(i) + " was not given a mapping");
1016 }
1017 xml.writeStartElement("Matrix");
1018 m_fileMetaData.writeCiftiXML2(xml);
1019 vector<bool> used(numDims, false);
1020 for (int i = 0; i < numDims; ++i)
1021 {
1022 if (!used[i])
1023 {
1024 used[i] = true;
1025 AString appliesTo = AString_number(i);//initialize containing just the current dimension
1026 for (int j = i + 1; j < numDims; ++j)//compare to all later unused dimensions for deduplication
1027 {//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
1028 if (!used[j])
1029 {
1030 if ((*m_indexMaps[i]) == (*m_indexMaps[j]))
1031 {
1032 appliesTo += "," + AString_number(j);
1033 used[j] = true;
1034 }
1035 }
1036 }
1037 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
1038 xml.writeAttribute("AppliesToMatrixDimension", appliesTo);
1039 m_indexMaps[i]->writeXML2(xml);
1040 xml.writeEndElement();
1041 }
1042 }
1043 xml.writeEndElement();
1044 }
0 #ifndef __CIFTI_XML_H__
1 #define __CIFTI_XML_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiMappingType.h"
31 #include "CiftiVersion.h"
32 #include "MetaData.h"
33
34 #include "CiftiBrainModelsMap.h"
35 #include "CiftiLabelsMap.h"
36 #include "CiftiParcelsMap.h"
37 #include "CiftiScalarsMap.h"
38 #include "CiftiSeriesMap.h"
39
40 #include "boost/shared_ptr.hpp"
41
42 #include <vector>
43
44 namespace cifti
45 {
46 ///class for retrieving and setting mapping information of cifti files
47 class CiftiXML
48 {
49 public:
50 enum
51 {
52 ALONG_ROW = 0,
53 ALONG_COLUMN = 1,
54 ALONG_STACK = 2//better name for this?
55 };
56 int getNumberOfDimensions() const { return m_indexMaps.size(); }
57 const CiftiVersion& getParsedVersion() const { return m_parsedVersion; }
58
59 ///can return null in unfilled XML object
60 const CiftiMappingType* getMap(const int& direction) const;
61
62 ///can return null in unfilled XML object
63 CiftiMappingType* getMap(const int& direction);
64 const MetaData& getFileMetaData() const;
65
66 CiftiMappingType::MappingType getMappingType(const int& direction) const;//convenience functions
67 const CiftiBrainModelsMap& getBrainModelsMap(const int& direction) const;
68 CiftiBrainModelsMap& getBrainModelsMap(const int& direction);
69 const CiftiLabelsMap& getLabelsMap(const int& direction) const;
70 CiftiLabelsMap& getLabelsMap(const int& direction);
71 const CiftiParcelsMap& getParcelsMap(const int& direction) const;
72 CiftiParcelsMap& getParcelsMap(const int& direction);
73 const CiftiScalarsMap& getScalarsMap(const int& direction) const;
74 CiftiScalarsMap& getScalarsMap(const int& direction);
75 const CiftiSeriesMap& getSeriesMap(const int& direction) const;
76 CiftiSeriesMap& getSeriesMap(const int& direction);
77 int64_t getDimensionLength(const int& direction) const;
78 std::vector<int64_t> getDimensions() const;
79
80 void setNumberOfDimensions(const int& num);
81 void setMap(const int& direction, const CiftiMappingType& mapIn);
82 void setFileMetaData(const MetaData& mdIn) { m_fileMetaData = mdIn; }
83 void clear();
84
85 void readXML(XmlReader& xml);
86 void readXML(const std::vector<char>& text);
87
88 std::vector<char> writeXMLToVector(const CiftiVersion& writingVersion = CiftiVersion()) const;
89 void writeXML(XmlWriter& xml, const CiftiVersion& writingVersion = CiftiVersion()) const;
90
91 ///uses the mapping types to figure out what the intent info should be
92 int32_t getIntentInfo(const CiftiVersion& writingVersion, char intentNameOut[16]) const;
93
94 CiftiXML() { }
95 CiftiXML(const CiftiXML& rhs);
96 CiftiXML& operator=(const CiftiXML& rhs);
97 bool operator==(const CiftiXML& rhs) const;
98 bool operator!=(const CiftiXML& rhs) const { return !((*this) == rhs); }
99 bool approximateMatch(const CiftiXML& rhs) const;
100 private:
101 std::vector<boost::shared_ptr<CiftiMappingType> > m_indexMaps;
102 CiftiVersion m_parsedVersion;
103 MetaData m_fileMetaData;
104
105 void copyHelper(const CiftiXML& rhs);
106 //parsing functions
107 void parseCIFTI1(XmlReader& xml);
108 void parseMatrix1(XmlReader& xml);
109 void parseCIFTI2(XmlReader& xml);
110 void parseMatrix2(XmlReader& xml);
111 void parseMatrixIndicesMap1(XmlReader& xml);
112 void parseMatrixIndicesMap2(XmlReader& xml);
113 //writing functions
114 void writeMatrix1(XmlWriter& xml) const;
115 void writeMatrix2(XmlWriter& xml) const;
116 };
117 }
118
119 #endif //__CIFTI_XML_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sstream>
28
29 #include "Label.h"
30
31 #include <limits>
32
33 using namespace cifti;
34
35 const int32_t Label::s_invalidLabelKey = std::numeric_limits<int32_t>::min();
36
37 /**
38 * Constructor.
39 *
40 * @param key - key of the label.
41 * @param name - name of label.
42 *
43 */
44 Label::Label(
45 const int32_t key,
46 const AString& name)
47 {
48 this->initializeMembersLabel();
49 this->key = key;
50 this->name = name;
51 }
52
53 /**
54 * Constructor.
55 *
56 * @param key - Key of the label.
57 * @param name - name of label.
58 * @param red - red color component, zero to one.
59 * @param green - green color component, zero to one.
60 * @param blue - blue color component, zero to one.
61 * @param alpha - alpha color component, zero to one.
62 *
63 */
64 Label::Label(
65 const int32_t key,
66 const AString& name,
67 const float red,
68 const float green,
69 const float blue,
70 const float alpha)
71 {
72 this->initializeMembersLabel();
73 this->key = key;
74 this->name = name;
75 this->red = red;
76 this->green = green;
77 this->blue = blue;
78 this->alpha = alpha;
79 }
80
81 /**
82 * Constructor.
83 *
84 * @param key - Key of the label.
85 * @param name - name of label.
86 * @param red - red color component, zero to one.
87 * @param green - green color component, zero to one.
88 * @param blue - blue color component, zero to one.
89 * @param alpha - alpha color component, zero to one.
90 *
91 */
92 Label::Label(
93 const int32_t key,
94 const AString& name,
95 const double red,
96 const double green,
97 const double blue,
98 const double alpha)
99 {
100 this->initializeMembersLabel();
101 this->key = key;
102 this->name = name;
103 this->red = red;
104 this->green = green;
105 this->blue = blue;
106 this->alpha = alpha;
107 }
108
109 /**
110 * Constructor.
111 *
112 * @param key - Key of the label.
113 * @param name - name of label.
114 * @param rgba - red, green, blue, alpha color componenents, zero to one.
115 *
116 */
117 Label::Label(
118 const int32_t key,
119 const AString& name,
120 const float rgba[])
121 {
122 this->initializeMembersLabel();
123 this->key = key;
124 this->name = name;
125 this->red = rgba[0];
126 this->green = rgba[1];
127 this->blue = rgba[2];
128 this->alpha = rgba[3];
129 }
130
131 /**
132 * Constructor.
133 *
134 * @param key - Key of the label.
135 * @param name - name of label.
136 * @param red - red color component, zero to two-fifty-five.
137 * @param green - green color component, zero to two-fifty-five.
138 * @param blue - blue color component, zero to two-fifty-five.
139 * @param alpha - alpha color component, zero to two-fifty-five.
140 *
141 */
142 Label::Label(
143 const int32_t key,
144 const AString& name,
145 const int32_t red,
146 const int32_t green,
147 const int32_t blue,
148 const int32_t alpha)
149 {
150 this->initializeMembersLabel();
151 this->key = key;
152 this->name = name;
153 this->red = red / 255.0;
154 this->green = green / 255.0;
155 this->blue = blue / 255.0;
156 this->alpha = alpha / 255.0;
157 }
158
159 /**
160 * Constructor.
161 *
162 * @param key - Key of the label.
163 * @param name - name of label.
164 * @param rgba - red, green, blue, alpha color componenents, zero to 255.
165 *
166 */
167 Label::Label(
168 const int32_t key,
169 const AString& name,
170 const int32_t rgba[])
171 {
172 this->initializeMembersLabel();
173 this->key = key;
174 this->name = name;
175 this->red = rgba[0] / 255.0;
176 this->green = rgba[1] / 255.0;
177 this->blue = rgba[2] / 255.0;
178 this->alpha = rgba[3] / 255.0;
179 }
180
181 /**
182 * Constructor.
183 *
184 * @param key - Key of the label.
185 *
186 */
187 Label::Label(
188 const int32_t key)
189 {
190 this->initializeMembersLabel();
191 this->key = key;
192 if (this->key == 0) {
193 this->name = "???";
194 }
195 else {
196 std::stringstream str;
197 str << "???" << this->key;
198 this->name = str.str().c_str();
199 }
200 }
201
202 Label::~Label()
203 {
204 }
205
206 Label::Label(const Label& o)
207 {
208 this->initializeMembersLabel();
209 this->copyHelper(o);
210 }
211
212 Label&
213 Label::operator=(const Label& o)
214 {
215 if (this != &o) {
216 this->copyHelper(o);
217 };
218 return *this;
219 }
220
221 /**
222 * Helps with copy constructor and assignment operator.
223 */
224 void
225 Label::copyHelper(const Label& gl)
226 {
227 this->initializeMembersLabel();
228 this->name = gl.name;
229 this->key = gl.key;
230 this->selected = gl.selected;
231 this->red = gl.red;
232 this->green = gl.green;
233 this->blue = gl.blue;
234 this->alpha = gl.alpha;
235 }
236
237 /**
238 * Initialize data members.
239 */
240 void
241 Label::initializeMembersLabel()
242 {
243 this->name = "";
244 this->key = s_invalidLabelKey;
245 this->selected = true;
246 this->red = 1.0;
247 this->green = 1.0;
248 this->blue = 1.0;
249 this->alpha = 1.0;
250 }
251
252 /**
253 * Determine if two labels are equal. Two Labels are equal if they
254 * have the same "key".
255 * @param gl label for comparison.
256 * @return true if equal, else false.
257 *
258 */
259 bool
260 Label::equals(const Label& gl)
261 {
262 return (this->key == gl.key);
263 }
264
265 /**
266 * Compare this label to another label using the indices of the labels.
267 * @param gl - Compare to this Label.
268 * @return negative if "this" is less, positive if "this" is greater,
269 * else zero.
270 *
271 */
272 int32_t
273 Label::operator<(const Label& gl)
274 {
275 return (this->key < gl.key);
276 }
277
278 /**
279 * Get the key of this label.
280 * @return key of the label.
281 *
282 */
283 int32_t
284 Label::getKey() const
285 {
286 return this->key;
287 }
288
289 /**
290 * Set the key of this label. DO NOT call this method on a label
291 * retrieved from the label table.
292 *
293 * @param key - New key for this label.
294 *
295 */
296 void
297 Label::setKey(const int32_t key)
298 {
299 this->key = key;
300 }
301
302 /**
303 * Get the name.
304 * @return Name of label.
305 *
306 */
307 AString
308 Label::getName() const
309 {
310 return this->name;
311 }
312
313 /**
314 * Set the name.
315 * @param name - new name for label.
316 *
317 */
318 void
319 Label::setName(const AString& name)
320 {
321 this->name = name;
322 }
323
324 /**
325 * Is this label selected (for display)?
326 *
327 * @return true if label selected for display, else false.
328 *
329 */
330 bool
331 Label::isSelected() const
332 {
333 return this->selected;
334 }
335
336 /**
337 * Set the label selected (for display).
338 *
339 * @param selected - new selection status.
340 *
341 */
342 void
343 Label::setSelected(const bool selected)
344 {
345 this->selected = selected;
346 }
347
348 /**
349 * Get the color components.
350 *
351 * @return A four-dimensional array of floats containing the red, green,
352 * blue, and alpha components with values ranging from 0.0 to 1.0.
353 * User MUST delete[] returned array.
354 *
355 */
356 float*
357 Label::getColor() const
358 {
359 float* rgba = new float[4];
360 rgba[0] = this->red;
361 rgba[1] = this->green;
362 rgba[2] = this->blue;
363 rgba[3] = this->alpha;
364 return rgba;
365 }
366
367 /**
368 * Get the color components.
369 * @param rgbaOut four dimensional array into which are loaded,
370 * red, green, blue, and alpha components ranging 0.0. to 1.0.
371 *
372 */
373 void
374 Label::getColor(float rgbaOut[]) const
375 {
376 rgbaOut[0] = this->red;
377 rgbaOut[1] = this->green;
378 rgbaOut[2] = this->blue;
379 rgbaOut[3] = this->alpha;
380 }
381
382 /**
383 * Set the color components.
384 *
385 * @param rgba - A four-dimensional array of floats containing the red,
386 * green, blue, and alpha components with values ranging from 0.0 to 1.0.
387 *
388 */
389 void
390 Label::setColor(const float rgba[])
391 {
392 this->red = rgba[0];
393 this->green = rgba[1];
394 this->blue = rgba[2];
395 this->alpha = rgba[3];
396 }
397
398 /**
399 * Get the colors as integers ranging 0 to 255.
400 * @return Four-dimensional array containing color components.
401 * User must delete[] the returned array.
402 *
403 */
404 int32_t*
405 Label::getColorInt() const
406 {
407 int32_t* rgbaOut = new int32_t[4];
408 rgbaOut[0] = static_cast<int32_t>(this->red * 255);
409 rgbaOut[1] = static_cast<int32_t>(this->green * 255);
410 rgbaOut[2] = static_cast<int32_t>(this->blue * 255);
411 rgbaOut[3] = static_cast<int32_t>(this->alpha * 255);
412 return rgbaOut;
413 }
414
415 /**
416 * Set the colors with integers ranging 0 to 255.
417 * @param rgba - four-dimensional array with colors ranging 0 to 255.
418 *
419 */
420 void
421 Label::setColorInt(const int32_t rgba[])
422 {
423 this->red = rgba[0] / 255.0;
424 this->green = rgba[1] / 255.0;
425 this->blue = rgba[2] / 255.0;
426 this->alpha = rgba[3] / 255.0;
427 }
428
429 /**
430 * Get the default color.
431 *
432 * @param rgbaOut ouput, a four-dimensional array of floats
433 * containing the red, green, blue, and alpha components with values
434 * ranging from 0.0 to 1.0.
435 */
436 void
437 Label::getDefaultColor(float rgbaOut[4])
438 {
439 rgbaOut[0] = 1.0;
440 rgbaOut[1] = 1.0;
441 rgbaOut[2] = 1.0;
442 rgbaOut[3] = 1.0;
443 }
444
445 /**
446 * Get the red color component for this label.
447 * @return red color component.
448 *
449 */
450 float
451 Label::getRed() const
452 {
453 return this->red;
454 }
455
456 /**
457 * Get the green color component for this label.
458 * @return green color component.
459 *
460 */
461 float
462 Label::getGreen() const
463 {
464 return this->green;
465 }
466
467 /**
468 * Get the blue color component for this label.
469 * @return blue color component.
470 *
471 */
472 float
473 Label::getBlue() const
474 {
475 return this->blue;
476 }
477
478 /**
479 * Get the alpha color component for this label.
480 * @return alpha color component.
481 *
482 */
483 float
484 Label::getAlpha() const
485 {
486 return this->alpha;
487 }
488
489 bool
490 Label::matches(const Label& rhs, const bool checkColor) const
491 {
492 if (key != rhs.key) return false;
493 if (name != rhs.name) return false;
494 if (checkColor)
495 {
496 if (red != rhs.red) return false;
497 if (green != rhs.green) return false;
498 if (blue != rhs.blue) return false;
499 if (alpha != rhs.alpha) return false;
500 }
501 return true;
502 }
0 #ifndef __LABEL_H__
1 #define __LABEL_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/AString.h"
31
32 #include <stdint.h>
33
34 namespace cifti {
35
36 class GroupAndNameHierarchyItem;
37
38 class Label {
39
40 public:
41 Label(
42 const int32_t key,
43 const AString& name);
44
45 explicit Label(
46 const int32_t key,
47 const AString& name,
48 const float red,
49 const float green,
50 const float blue,
51 const float alpha);
52
53 explicit Label(
54 const int32_t key,
55 const AString& name,
56 const double red,
57 const double green,
58 const double blue,
59 const double alpha);
60
61 Label(
62 const int32_t key,
63 const AString& name,
64 const float rgba[]);
65
66 explicit Label(
67 const int32_t key,
68 const AString& name,
69 const int32_t red,
70 const int32_t green,
71 const int32_t blue,
72 const int32_t alpha);
73
74 Label(
75 const int32_t key,
76 const AString& name,
77 const int32_t rgba[]);
78
79 Label(const int32_t key);
80
81 Label(const Label& gl);
82
83 public:
84 Label& operator=(const Label& gl);
85
86 virtual ~Label();
87
88 private:
89 void copyHelper(const Label& o);
90
91 void initializeMembersLabel();
92
93 public:
94 int32_t hashCode();
95
96 bool equals(const Label&);
97
98 int32_t operator<(const Label& gl);
99
100 int32_t getKey() const;
101
102 void setKey(const int32_t key);
103
104 AString getName() const;
105
106 void setName(const AString& name);
107
108 bool isSelected() const;
109
110 void setSelected(const bool selected);
111
112 float* getColor() const;
113
114 void getColor(float rgbaOut[]) const;
115
116 void setColor(const float rgba[]);
117
118 int32_t* getColorInt() const;
119
120 void setColorInt(const int32_t rgba[]);
121
122 static void getDefaultColor(float rgbaOut[4]);
123
124 float getRed() const;
125
126 float getGreen() const;
127
128 float getBlue() const;
129
130 float getAlpha() const;
131
132 bool matches(const Label& rhs, const bool checkColor = false) const;
133
134 /**
135 * @return The invalid label key.
136 */
137 static inline int32_t getInvalidLabelKey() { return s_invalidLabelKey; }
138
139 private:
140 AString name;
141
142 int32_t key;
143
144 bool selected;
145
146 float red;
147
148 float green;
149
150 float blue;
151
152 float alpha;
153
154 /** The invalid label key */
155 const static int32_t s_invalidLabelKey;
156 };
157
158 } // namespace
159
160 #endif // __LABEL_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <algorithm>
28 #include <sstream>
29 #include <iostream>
30
31 #include "Common/CiftiAssert.h"
32 #include "Label.h"
33 #include "LabelTable.h"
34
35 using namespace std;
36 using namespace cifti;
37
38 LabelTable::LabelTable()
39 {
40 clear();//actually adds the 0: ??? label
41 }
42
43 LabelTable::~LabelTable()
44 {
45 for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin();
46 iter != labelsMap.end();
47 iter++) {
48 delete iter->second;
49 }
50 this->labelsMap.clear();
51 }
52
53 LabelTable::LabelTable(const LabelTable& glt)
54 {
55 this->copyHelper(glt);
56 }
57
58 LabelTable&
59 LabelTable::operator=(const LabelTable& glt)
60 {
61 if (this != &glt) {
62 this->copyHelper(glt);
63 };
64 return *this;
65 }
66
67 /**
68 * Helps with copy constructor and assignment operator.
69 */
70 void
71 LabelTable::copyHelper(const LabelTable& glt)
72 {
73 this->clear();
74
75 for (LABELS_MAP_CONST_ITERATOR iter = glt.labelsMap.begin();
76 iter != glt.labelsMap.end();
77 iter++) {
78 Label* myLabel = this->getLabel(iter->second->getKey());
79 if (myLabel != NULL)
80 {
81 *myLabel = *(iter->second);
82 } else {
83 addLabel(iter->second);
84 }
85 }
86 }
87
88 /**
89 * Clear the labelTable.
90 *
91 */
92 void
93 LabelTable::clear()
94 {
95 for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin();
96 iter != labelsMap.end();
97 iter++) {
98 delete iter->second;
99 }
100 this->labelsMap.clear();
101
102 Label gl(0, "???", 1.0, 1.0, 1.0, 0.0);
103 this->addLabel(&gl);
104 }
105
106 /**
107 * Append a label table to this label table. Since labels may be
108 * duplicated, the map returned that converts the keys of
109 * the appended LabelTable to keys for "this" label table.
110 *
111 * @param glt Label table that is to be appended.
112 *
113 * @return A map where the keys are the keys in the label table
114 * that is passed as a parameter and the values are the keys
115 * into "this" label table.
116 *
117 */
118 std::map<int32_t,int32_t>
119 LabelTable::append(const LabelTable& glt)
120 {
121 std::map<int32_t,int32_t> keyConverterMap;
122
123 for (LABELS_MAP_CONST_ITERATOR iter = glt.labelsMap.begin();
124 iter != glt.labelsMap.end();
125 iter++) {
126 int32_t key = iter->first;
127 int32_t newKey = this->addLabel(iter->second);
128
129 keyConverterMap.insert(std::make_pair(key, newKey));
130 }
131 return keyConverterMap;
132 }
133
134 /**
135 * Add a label. If a label with the name exists, its colors
136 * are replaced with these color components.
137 * @param labelName Name of label.
138 * @param red Red color component ranging 0.0 to 1.0.
139 * @param green Green color component ranging 0.0 to 1.0.
140 * @param blue Blue color component ranging 0.0 to 1.0.
141 * @param alpha Alpha color component ranging 0.0 to 1.0.
142 * @return Index of the existing label, or, if no label
143 * exists with name, index of new label.
144 *
145 */
146 int32_t
147 LabelTable::addLabel(
148 const AString& labelName,
149 const float red,
150 const float green,
151 const float blue,
152 const float alpha)
153 {
154 const Label gl(Label::getInvalidLabelKey(), labelName, red, green, blue, alpha);
155 return this->addLabel(&gl);
156 }
157
158 /**
159 * Add a label. If a label with the name exists, its colors
160 * are replaced with these color components.
161 * @param labelName Name of label.
162 * @param red Red color component ranging 0.0 to 1.0.
163 * @param green Green color component ranging 0.0 to 1.0.
164 * @param blue Blue color component ranging 0.0 to 1.0.
165 * @return Index of the existing label, or, if no label
166 * exists with name, index of new label.
167 *
168 */
169 int32_t
170 LabelTable::addLabel(
171 const AString& labelName,
172 const float red,
173 const float green,
174 const float blue)
175 {
176 return this->addLabel(labelName, red, green, blue, 1.0f);
177 }
178
179 /**
180 * Add a label. If a label with the name exists, its colors
181 * are replaced with these color components.
182 * @param labelName Name of label.
183 * @param red Red color component ranging 0 to 255.
184 * @param green Green color component ranging 0 to 255.
185 * @param blue Blue color component ranging 0 to 255.
186 * @param alpha Alpha color component ranging 0 to 255.
187 * @return Index of the existing label, or, if no label
188 * exists with name, index of new label.
189 *
190 */
191 int32_t
192 LabelTable::addLabel(
193 const AString& labelName,
194 const int32_t red,
195 const int32_t green,
196 const int32_t blue,
197 const int32_t alpha)
198 {
199 const Label gl(Label::getInvalidLabelKey(), labelName, red, green, blue, alpha);
200 return this->addLabel(&gl);
201 }
202
203 /**
204 * Add a label. If a label with the name exists, its colors
205 * are replaced with these color components.
206 * @param labelName Name of label.
207 * @param red Red color component ranging 0 to 255.
208 * @param green Green color component ranging 0 to 255.
209 * @param blue Blue color component ranging 0 to 255.
210 * @return Index of the existing label, or, if no label
211 * exists with name, index of new label.
212 *
213 */
214 int32_t
215 LabelTable::addLabel(
216 const AString& labelName,
217 const int32_t red,
218 const int32_t green,
219 const int32_t blue)
220 {
221 return this->addLabel(labelName, red, green, blue, 255);
222 }
223
224 /**
225 * Add a label to the label table. If the label's key is already in
226 * the label table, a new key is created. If a label of the same
227 * name already exists, the key of the existing label is returned
228 * and its color is overridden.
229 * @param glIn - Label to add.
230 * @return Key of the label, possibly different than its original key.
231 *
232 */
233 int32_t
234 LabelTable::addLabel(const Label* glIn)
235 {
236 /*
237 * First see if a label with the same name already exists
238 */
239 int32_t key = this->getLabelKeyFromName(glIn->getName());
240
241 /*
242 * If no label with the name exists, get the key
243 * (which may be invalid) from the input label,
244 * and check that nothing uses that key
245 */
246 if (key == Label::getInvalidLabelKey()) {
247 int32_t tempkey = glIn->getKey();
248 LABELS_MAP_ITERATOR iter = this->labelsMap.find(tempkey);
249 if (iter == labelsMap.end())
250 {
251 key = tempkey;
252 }
253 }
254
255 /*
256 * Still need a key, find an unused key
257 */
258 if (key == Label::getInvalidLabelKey()) {
259 key = this->generateUnusedKey();
260
261 Label* gl = new Label(*glIn);
262 gl->setKey(key);
263 this->labelsMap.insert(std::make_pair(key, gl));
264 return key;
265 }
266
267 LABELS_MAP_ITERATOR iter = this->labelsMap.find(key);
268 if (iter != this->labelsMap.end()) {
269 /*
270 * Update existing label
271 */
272 Label* gl = iter->second;
273 gl->setName(glIn->getName());
274 float rgba[4];
275 glIn->getColor(rgba);
276 gl->setColor(rgba);
277 key = iter->first;
278 }
279 else {
280 /*
281 * Insert a new label
282 */
283 this->labelsMap.insert(std::make_pair(key, new Label(*glIn)));
284 }
285 return key;
286 }
287
288 /**
289 * Generate an unused key.
290 * @return An unused key.
291 */
292 int32_t
293 LabelTable::generateUnusedKey() const
294 {
295 const int32_t numKeys = labelsMap.size();
296 LABELS_MAP::const_reverse_iterator rbegin = labelsMap.rbegin();//reverse begin is largest key
297 if (numKeys > 0 && rbegin->first > 0)//there is at least one positive key
298 {
299 if (rbegin->first < numKeys)
300 {
301 CiftiAssert(labelsMap.find(rbegin->first + 1) == labelsMap.end());
302 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
303 } else {
304 LABELS_MAP::const_iterator begin = labelsMap.begin();
305 if (begin->first == 1 && rbegin->first == numKeys)
306 {
307 CiftiAssert(labelsMap.find(rbegin->first + 1) == labelsMap.end());
308 return rbegin->first + 1;//keys are compact but missing 0, do not return 0, so return next
309 } else {//there aren't enough negatives to make up for the missing, search for a hole in the positives
310 LABELS_MAP::const_iterator iter = labelsMap.upper_bound(0);//start with first positive
311 int32_t curVal = 0;//if it isn't one, we can stop early
312 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
313 {
314 curVal = iter->first;
315 ++iter;
316 }
317 CiftiAssert(labelsMap.find(curVal + 1) == labelsMap.end());
318 return curVal + 1;
319 }
320 }
321 } else {
322 CiftiAssert(labelsMap.find(1) == labelsMap.end());
323 return 1;//otherwise, no keys exist or all keys are non-positive, return 1
324 }
325 }
326
327 /**
328 * Remove the label with the specified key.
329 * @param key - key of label.
330 *
331 */
332 void
333 LabelTable::deleteLabel(const int32_t key)
334 {
335 if (key == 0)
336 {//key 0 is reserved (sort of)
337 cerr << "Label 0 DELETED!" << endl;
338 }
339 LABELS_MAP_ITERATOR iter = this->labelsMap.find(key);
340 if (iter != this->labelsMap.end()) {
341 this->labelsMap.erase(iter);
342 delete iter->second;
343 }
344 }
345
346 /**
347 * Remove a label from the label table.
348 * This method WILL DELETE the label passed
349 * in so the caller should never use the parameter
350 * passed after this call.
351 * @param label - label to remove.
352 *
353 */
354 void
355 LabelTable::deleteLabel(const Label* label)
356 {
357 if (label->getKey() == 0)
358 {//key 0 is reserved (sort of)
359 cerr << "Label 0 DELETED!" << endl;
360 }
361 for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin();
362 iter != this->labelsMap.end();
363 iter++) {
364 if (iter->second == label) {
365 this->labelsMap.erase(iter);
366 break;
367 }
368 }
369 delete label;
370 }
371
372 /**
373 * Remove unused labels from the label table. Note that the unassigned
374 * label is not removed, even if it is unused.
375 *
376 * @param usedLabelKeys - Color keys that are in use.
377 *
378 */
379 void
380 LabelTable::deleteUnusedLabels(const std::set<int32_t>& usedLabelKeys)
381 {
382 LABELS_MAP newMap;
383 int32_t unassignedKey = getUnassignedLabelKey();
384 for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin();
385 iter != this->labelsMap.end();
386 iter++) {
387 int32_t key = iter->first;
388 Label* gl = iter->second;
389 if (key == unassignedKey || usedLabelKeys.find(key) != usedLabelKeys.end()) {//unassigned key gets a free pass
390 newMap.insert(std::make_pair(key, gl));
391 }
392 else {
393 delete gl;
394 }
395 }
396
397 this->labelsMap = newMap;
398 }
399
400 /**
401 * Insert the label using the labels key.
402 * @param labelIn - Label to insert (replaces an existing label
403 * with the same key).
404 *
405 */
406 void
407 LabelTable::insertLabel(const Label* labelIn)
408 {
409 Label* label = new Label(*labelIn);
410 int32_t key = label->getKey();
411 if (key == Label::getInvalidLabelKey()) {
412 key = this->generateUnusedKey();
413 label->setKey(key);
414 }
415 /*
416 * Note: A map DOES NOT replace an existing key, so it
417 * must be deleted and then added.
418 */
419 LABELS_MAP_ITERATOR keyPos = this->labelsMap.find(label->getKey());
420 if (keyPos != this->labelsMap.end()) {
421 Label* gl = keyPos->second;
422 this->labelsMap.erase(keyPos);
423 delete gl;
424 }
425
426 this->labelsMap.insert(std::make_pair(label->getKey(), label));
427 }
428
429 /**
430 * Get the key of a lable from its name.
431 * @param name Name to search for.
432 * @return Key of Name or Label::getInvalidLabelKey() if not found.
433 *
434 */
435 int32_t
436 LabelTable::getLabelKeyFromName(const AString& name) const
437 {
438 LABELS_MAP newMap;
439 for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin();
440 iter != this->labelsMap.end();
441 iter++) {
442 int32_t key = iter->first;
443 Label* gl = iter->second;
444 if (gl->getName() == name) {
445 return key;
446 }
447 }
448 return Label::getInvalidLabelKey();
449 }
450
451 /**
452 * Get a Label from its name.
453 * @param labelName - Name of label that is sought.
454 * @return Reference to label with name or null if no matching label.
455 *
456 */
457 const Label*
458 LabelTable::getLabel(const AString& labelName) const
459 {
460 LABELS_MAP newMap;
461 for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin();
462 iter != this->labelsMap.end();
463 iter++) {
464 Label* gl = iter->second;
465 if (gl->getName() == labelName) {
466 return gl;
467 }
468 }
469 return NULL;
470 }
471
472 /**
473 * Get a Label from its name.
474 * @param labelName - Name of label that is sought.
475 * @return Reference to label with name or null if no matching label.
476 *
477 */
478 Label*
479 LabelTable::getLabel(const AString& labelName)
480 {
481 LABELS_MAP newMap;
482 for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin();
483 iter != this->labelsMap.end();
484 iter++) {
485 Label* gl = iter->second;
486 if (gl->getName() == labelName) {
487 return gl;
488 }
489 }
490 return NULL;
491 }
492
493 /**
494 * Get the Label at the specified key.
495 *
496 * @param key - Key of Label entry.
497 * @return The Label at the specified key or null if the
498 * there is not a label at the specified key.
499 *
500 */
501 const Label*
502 LabelTable::getLabel(const int32_t key) const
503 {
504 LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.find(key);
505 if (iter != this->labelsMap.end()) {
506 return iter->second;
507 }
508 return NULL;
509 }
510
511 /**
512 * Get the Label at the specified key.
513 *
514 * @param key - Key of Label entry.
515 * @return The Label at the specified key or null if the
516 * there is not a label at the specified key.
517 */
518 Label*
519 LabelTable::getLabel(const int32_t key)
520 {
521 LABELS_MAP_ITERATOR iter = this->labelsMap.find(key);
522 if (iter != this->labelsMap.end()) {
523 return iter->second;
524 }
525 return NULL;
526 }
527
528 /**
529 * Get the key for the unassigned label.
530 * @return Index of key for unassigned label.
531 * A valid key will always be returned.
532 *
533 */
534 int32_t
535 LabelTable::getUnassignedLabelKey() const
536 {
537 const Label* gl = this->getLabel("???");
538 if (gl != NULL) {
539 return gl->getKey();
540 }
541
542 /*
543 * Remove 'constness' from this object so that the
544 * label can be added.
545 */
546 LabelTable* glt = (LabelTable*)this;
547 const int32_t key = glt->addLabel("???", 0.0f, 0.0f, 0.0f, 0.0f);
548 return key;
549 }
550
551 /**
552 * Get the number of labels. This value is one greater than the last
553 * label key. Note that not every key may have a label. If there
554 * are no labels this returns 0.
555 * @return Number of labels.
556 *
557 */
558 int32_t
559 LabelTable::getNumberOfLabels() const
560 {
561 return this->labelsMap.size();
562 }
563
564 /**
565 * Get the name of the label at the key. If there is no label at the
566 * key an empty string is returned.
567 * @param key - key of label.
568 * @return Name of label at inkeydex.
569 *
570 */
571 AString
572 LabelTable::getLabelName(const int32_t key) const
573 {
574 LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.find(key);
575 if (iter != this->labelsMap.end()) {
576 const AString name = iter->second->getName();
577 return name;
578 }
579 return "";
580 }
581
582 /**
583 * Set the name of a label.
584 * @param key - key of label.
585 * @param name - new name of label.
586 *
587 */
588 void
589 LabelTable::setLabelName(
590 const int32_t key,
591 const AString& name)
592 {
593 LABELS_MAP_ITERATOR iter = this->labelsMap.find(key);
594 if (iter != this->labelsMap.end()) {
595 iter->second->setName(name);
596 }
597 }
598
599 /**
600 * Set a label. If a label with the specified key exists,
601 * it is replaced.
602 *
603 * @param key Key for label.
604 * @param name Name of label.
605 * @param red Red color component.
606 * @param green Green color component.
607 * @param blue Blue color component.
608 * @param alpha Alpha color component.
609 *
610 */
611 void
612 LabelTable::setLabel(
613 const int32_t key,
614 const AString& name,
615 const float red,
616 const float green,
617 const float blue,
618 const float alpha)
619 {
620 LABELS_MAP_ITERATOR iter = this->labelsMap.find(key);
621 if (iter != this->labelsMap.end()) {
622 Label* gl = iter->second;
623 gl->setName(name);
624 float rgba[4] = { red, green, blue, alpha };
625 gl->setColor(rgba);
626 }
627 else {
628 Label gl(key, name, red, green, blue, alpha);
629 this->addLabel(&gl);
630 }
631 }
632
633 /**
634 * Get the selection status of the label at the specified key. If there
635 * is no label at the key, false is returned.
636 * @param key - key of label
637 * @return selection status of label.
638 *
639 */
640 bool
641 LabelTable::isLabelSelected(const int32_t key) const
642 {
643 LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.find(key);
644 if (iter != this->labelsMap.end()) {
645 return iter->second->isSelected();
646 }
647 return false;
648 }
649
650 /**
651 * Set the selection status of a label.
652 * @param key - key of label.
653 * @param sel - new selection status.
654 *
655 */
656 void
657 LabelTable::setLabelSelected(
658 const int32_t key,
659 const bool sel)
660 {
661 LABELS_MAP_ITERATOR iter = this->labelsMap.find(key);
662 if (iter != this->labelsMap.end()) {
663 iter->second->setSelected(sel);
664 }
665 }
666
667 /**
668 * Set the selection status for all labels.
669 * @param newStatus New selection status.
670 *
671 */
672 void
673 LabelTable::setSelectionStatusForAllLabels(const bool newStatus)
674 {
675 for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin();
676 iter != this->labelsMap.end();
677 iter++) {
678 Label* gl = iter->second;
679 gl->setSelected(newStatus);
680 }
681 }
682
683 /**
684 * Get the alpha color component for a label. If the key is not a
685 * valid label, an alpha of zero is returned.
686 * @param key - Key of label.
687 * @return Alpha for label or zero if invalid key.
688 *
689 */
690 float
691 LabelTable::getLabelAlpha(const int32_t key) const
692 {
693 const Label* gl = this->getLabel(key);
694 if (gl != NULL) {
695 return gl->getAlpha();
696 }
697 return 0.0;
698 }
699
700 /**
701 * Get the color for a label.
702 * @param key - key of label.
703 * @param rgbaOut - output, its color components
704 *
705 */
706 void
707 LabelTable::getLabelColor(const int32_t key, float rgbaOut[4]) const
708 {
709 const Label* gl = this->getLabel(key);
710 if (gl != NULL) {
711 gl->getColor(rgbaOut);
712 }
713 }
714
715 /**
716 * Set the color of a label.
717 * @param key - key of label.
718 * @param color - new color of label.
719 *
720 */
721 void
722 LabelTable::setLabelColor(
723 const int32_t key,
724 const float color[])
725 {
726 LABELS_MAP_ITERATOR iter = this->labelsMap.find(key);
727 if (iter != this->labelsMap.end()) {
728 Label* gl = iter->second;
729 gl->setColor(color);
730 }
731 }
732
733 void
734 LabelTable::writeXML(XmlWriter& xmlWriter) const
735 {
736 //
737 // Write the label tag
738 //
739 xmlWriter.writeStartElement("LabelTable");
740
741 //
742 // Write the labels
743 //
744 std::set<int32_t> keys = this->getKeys();
745 for (std::set<int32_t>::const_iterator iter = keys.begin();
746 iter != keys.end();
747 iter++) {
748 int key = *iter;
749 const Label* label = this->getLabel(key);
750 if (label != NULL) {
751 xmlWriter.writeStartElement("Label");
752 xmlWriter.writeAttribute("Key", AString_number(key));
753 float* rgba = label->getColor();
754 xmlWriter.writeAttribute("Red", AString_number(rgba[0]));
755 xmlWriter.writeAttribute("Green", AString_number(rgba[1]));
756 xmlWriter.writeAttribute("Blue", AString_number(rgba[2]));
757 xmlWriter.writeAttribute("Alpha", AString_number(rgba[3]));
758 xmlWriter.writeCharacters(label->getName());
759 xmlWriter.writeEndElement();
760 delete[] rgba;
761 }
762 }
763
764 //
765 // Write the closing label tag
766 //
767 xmlWriter.writeEndElement();
768 }
769
770 void LabelTable::readXml(XmlReader& xml)
771 {
772 clear();
773 #ifdef CIFTILIB_USE_QT
774 if (!xml.isStartElement() || xml.name() != "LabelTable")
775 {
776 throw CiftiException("tried to read LabelTable when current element is not LabelTable");
777 }
778 while (xml.readNextStartElement() && !xml.atEnd())
779 {
780 if (xml.name() != "Label")
781 {
782 throw CiftiException("unexpected element '" + xml.name().toString() + "' encountered in Label");
783 }
784 int key;
785 float rgba[4];
786 QXmlStreamAttributes myAttrs = xml.attributes();
787 bool ok = false;
788 AString temp = myAttrs.value("Key").toString();
789 key = temp.toInt(&ok);
790 if (!ok) throw CiftiException("Key attribute of Label missing or noninteger");
791 temp = myAttrs.value("Red").toString();
792 rgba[0] = temp.toFloat(&ok);
793 if (!ok) throw CiftiException("Red attribute of Label missing or not a number");
794 temp = myAttrs.value("Green").toString();
795 rgba[1] = temp.toFloat(&ok);
796 if (!ok) throw CiftiException("Green attribute of Label missing or not a number");
797 temp = myAttrs.value("Blue").toString();
798 rgba[2] = temp.toFloat(&ok);
799 if (!ok) throw CiftiException("Blue attribute of Label missing or not a number");
800 temp = myAttrs.value("Alpha").toString();
801 if (temp == "")
802 {
803 rgba[3] = 1.0f;
804 } else {
805 rgba[3] = temp.toFloat(&ok);
806 if (!ok) throw CiftiException("Alpha attribute of Label is not a number");
807 }
808 temp = xml.readElementText();
809 if (xml.hasError()) return;
810 setLabel(key, temp, rgba[0], rgba[1], rgba[2], rgba[3]);
811 }
812 #else
813 #ifdef CIFTILIB_USE_XMLPP
814 vector<AString> mandAttrs(4), optAttrs(1, "Alpha");
815 mandAttrs[0] = "Key";
816 mandAttrs[1] = "Red";
817 mandAttrs[2] = "Green";
818 mandAttrs[3] = "Blue";
819 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
820 while(!done && xml.read())
821 {
822 switch (xml.get_node_type())
823 {
824 case XmlReader::Element:
825 {
826 AString name = xml.get_local_name();
827 if (name == "Label")
828 {
829 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs, optAttrs);
830 int key;
831 float rgba[4];
832 bool ok = false;
833 key = AString_toInt(myAttrs.mandatoryVals[0], ok);
834 if (!ok) throw CiftiException("Key attribute of Label is not an integer");
835 rgba[0] = AString_toFloat(myAttrs.mandatoryVals[1], ok);
836 if (!ok) throw CiftiException("Red attribute of Label is not a number");
837 rgba[1] = AString_toFloat(myAttrs.mandatoryVals[2], ok);
838 if (!ok) throw CiftiException("Green attribute of Label is not a number");
839 rgba[2] = AString_toFloat(myAttrs.mandatoryVals[3], ok);
840 if (!ok) throw CiftiException("Blue attribute of Label is not a number");
841 if (myAttrs.optionalVals[0].present)
842 {
843 rgba[3] = AString_toFloat(myAttrs.optionalVals[0].value, ok);
844 if (!ok) throw CiftiException("Alpha attribute of Label is not a number");
845 } else {
846 rgba[3] = 1.0f;
847 }
848 AString name = XmlReader_readElementText(xml);
849 setLabel(key, name, rgba[0], rgba[1], rgba[2], rgba[3]);
850 } else {
851 throw CiftiException("unexpected element in LabelTable: " + name);
852 }
853 break;
854 }
855 case XmlReader::EndElement:
856 done = true;
857 break;
858 default:
859 break;
860 }
861 }
862 #else
863 #error "not implemented"
864 #endif
865 #endif
866 CiftiAssert(XmlReader_checkEndElement(xml, "LabelTable"));
867 }
868
869 /**
870 * Get the valid keys of the labels in ascending order.
871 * @return A Set containing the valid keys of the label in
872 * ascending order.
873 *
874 */
875 std::set<int32_t>
876 LabelTable::getKeys() const
877 {
878 std::set<int32_t> keys;
879 for (std::map<int32_t,Label*>::const_iterator iter = this->labelsMap.begin();
880 iter != this->labelsMap.end();
881 iter++) {
882 keys.insert(iter->first);
883 }
884 return keys;
885 }
886
887 void LabelTable::getKeys(std::vector<int32_t>& keysOut) const
888 {
889 keysOut.reserve(labelsMap.size());
890 for (std::map<int32_t,Label*>::const_iterator iter = this->labelsMap.begin();
891 iter != this->labelsMap.end();
892 iter++) {
893 keysOut.push_back(iter->first);
894 }
895 }
896
897 /**
898 * Get all keys and names.
899 *
900 * @param keysAndNamesOut
901 * Map containing the pairs of corresponding keys and names.
902 */
903 void
904 LabelTable::getKeysAndNames(std::map<int32_t, AString>& keysAndNamesOut) const
905 {
906 keysAndNamesOut.clear();
907
908 for (std::map<int32_t,Label*>::const_iterator iter = this->labelsMap.begin();
909 iter != this->labelsMap.end();
910 iter++) {
911 const Label* gl = iter->second;
912 keysAndNamesOut.insert(std::make_pair(iter->first,
913 gl->getName()));
914 }
915 }
916
917 bool LabelTable::matches(const LabelTable& rhs, const bool checkColors) const
918 {
919 if (labelsMap.size() != rhs.labelsMap.size()) return false;
920 for (LABELS_MAP::const_iterator iter = labelsMap.begin(); iter != labelsMap.end(); ++iter)
921 {
922 LABELS_MAP::const_iterator riter = rhs.labelsMap.find(iter->first);
923 if (riter == rhs.labelsMap.end()) return false;
924 if (!iter->second->matches(*(riter->second), checkColors)) return false;
925 }
926 return true;
927 }
0 #ifndef __LABELTABLE_H__
1 #define __LABELTABLE_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/AString.h"
31
32 #include <map>
33 #include <set>
34 #include <vector>
35 #include <stdint.h>
36
37 #include "Common/XmlAdapter.h"
38
39 namespace cifti {
40
41 class Label;
42
43 class LabelTable {
44
45 public:
46 LabelTable();
47
48 LabelTable(const LabelTable& glt);
49
50 LabelTable& operator=(const LabelTable& glt);
51
52 bool matches(const LabelTable& rhs, const bool checkColors = false) const;
53
54 bool operator==(const LabelTable& rhs) const { return matches(rhs, true); }
55
56 bool operator!=(const LabelTable& rhs) const { return !((*this) == rhs); }
57
58 virtual ~LabelTable();
59
60 private:
61 void copyHelper(const LabelTable& glt);
62
63 public:
64 void clear();
65
66 std::map<int32_t,int32_t> append(const LabelTable& glt);
67
68 int32_t addLabel(
69 const AString& labelName,
70 const float red,
71 const float green,
72 const float blue,
73 const float alpha);
74
75 int32_t addLabel(
76 const AString& labelName,
77 const float red,
78 const float green,
79 const float blue);
80
81 int32_t addLabel(
82 const AString& labelName,
83 const int32_t red,
84 const int32_t green,
85 const int32_t blue,
86 const int32_t alpha);
87
88 int32_t addLabel(
89 const AString& labelName,
90 const int32_t red,
91 const int32_t green,
92 const int32_t blue);
93
94 int32_t addLabel(const Label* glt);
95
96 void deleteLabel(const int32_t key);
97
98 void deleteLabel(const Label* label);
99
100 void deleteUnusedLabels(const std::set<int32_t>& usedLabelKeys);
101
102 void insertLabel(const Label* label);
103
104 int32_t getLabelKeyFromName(const AString& name) const;
105
106 const Label* getLabel(const AString& labelName) const;
107
108 Label* getLabel(const AString& labelName);
109
110 const Label* getLabel(const int32_t key) const;
111
112 Label* getLabel(const int32_t key);
113
114 int32_t getUnassignedLabelKey() const;
115
116 int32_t getNumberOfLabels() const;
117
118 AString getLabelName(const int32_t key) const;
119
120 void setLabelName(
121 const int32_t key,
122 const AString& name);
123
124 void setLabel(const int32_t key,
125 const AString& name,
126 const float red,
127 const float green,
128 const float blue,
129 const float alpha);
130
131 bool isLabelSelected(const int32_t key) const;
132
133 void setLabelSelected(
134 const int32_t key,
135 const bool sel);
136
137 void setSelectionStatusForAllLabels(const bool newStatus);
138
139 float getLabelAlpha(const int32_t key) const;
140
141 void getLabelColor(const int32_t key, float rgbaOut[4]) const;
142
143 void setLabelColor(
144 const int32_t key,
145 const float color[4]);
146
147 void createLabelsForKeys(const std::set<int32_t>& newKeys);
148
149 void writeXML(XmlWriter& xmlWriter) const;
150
151 void readXml(XmlReader& xml);
152
153 std::set<int32_t> getKeys() const;
154
155 void getKeys(std::vector<int32_t>& keysOut) const;
156
157 void getKeysAndNames(std::map<int32_t, AString>& keysAndNamesOut) const;
158
159 int32_t generateUnusedKey() const;
160
161 private:
162 typedef std::map<int32_t, Label*> LABELS_MAP;
163 typedef std::map<int32_t, Label*>::iterator LABELS_MAP_ITERATOR;
164 typedef std::map<int32_t, Label*>::const_iterator LABELS_MAP_CONST_ITERATOR;
165
166 LABELS_MAP labelsMap;
167
168 };
169
170 } // namespace
171
172 #endif // __LABELTABLE_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <algorithm>
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/CiftiException.h"
31 #include "MetaData.h"
32
33 using namespace cifti;
34
35 MetaData::MetaData()
36 {
37 this->initializeMembersMetaData();
38 }
39
40 MetaData::~MetaData()
41 {
42 }
43
44 MetaData::MetaData(const MetaData& o)
45 {
46 this->initializeMembersMetaData();
47 this->copyHelper(o);
48 }
49
50 MetaData&
51 MetaData::operator=(const MetaData& o)
52 {
53 if (this != &o) {
54 this->copyHelper(o);
55 };
56 return *this;
57 }
58
59 bool MetaData::operator==(const MetaData& rhs) const
60 {
61 return (metadata == rhs.metadata);
62 }
63
64 /**
65 * Helps with copy constructor and assignment operator.
66 */
67 void
68 MetaData::copyHelper(const MetaData& o)
69 {
70 this->metadata = o.metadata;
71 }
72
73 void
74 MetaData::initializeMembersMetaData()
75 {
76 }
77
78 /**
79 * Clear the metadata.
80 *
81 */
82 void
83 MetaData::clear()
84 {
85 metadata.clear();
86 }
87
88 /**
89 * Append the metadata to this metadata. A comment is always appended.
90 * Other metadata are added only if the name is not in "this" metadata.
91 *
92 * @param smd Metadata that is to be appended to "this".
93 *
94 */
95 void
96 MetaData::append(const MetaData& smd)
97 {
98 for (MetaDataConstIterator iter = smd.metadata.begin();
99 iter != smd.metadata.end();
100 iter++) {
101 this->set(iter->first, iter->second);
102 }
103 }
104
105 /**
106 * Clears this metadata and then copies all metadata from "smd"
107 *
108 * @param smd Metadata that is to be copied to "this".
109 *
110 */
111 void
112 MetaData::replace(const MetaData& smd)
113 {
114 this->metadata = smd.metadata;
115 }
116
117 /**
118 * Sets metadata. If a metadata entry named "name" already
119 * exists, it is replaced.
120 *
121 * @param name Name of metadata entry.
122 * @param value Value for metadata entry.
123 *
124 */
125 void
126 MetaData::set(const AString& name,
127 const AString& value)
128 {
129 MetaDataIterator namePos = this->metadata.find(name);
130 if (namePos != this->metadata.end()) {
131 if (namePos->second != value) {
132 namePos->second = value;
133 }
134 }
135 else {
136 this->metadata.insert(std::make_pair(name, value));
137 }
138 }
139
140 /**
141 * Set metadata with an integer value.
142 * @param name - name of metadata.
143 * @param value - value of metadata.
144 *
145 */
146 void
147 MetaData::setInt(
148 const AString& name,
149 const int32_t value)
150 {
151 AString s = AString_number(value);
152 this->set(name, s);
153 }
154
155 /**
156 * Set metadata with an float value.
157 * @param name - name of metadata.
158 * @param value - value of metadata.
159 *
160 */
161 void
162 MetaData::setFloat(
163 const AString& name,
164 const float value)
165 {
166 AString s = AString_number(value);
167 this->set(name, s);
168 }
169
170 /**
171 * Replace ALL of the metadata with the data in the given map.
172 *
173 * @param map
174 * New metadata that replaces all existing metadata.
175 */
176 void
177 MetaData::replaceWithMap(const std::map<AString, AString>& map)
178 {
179 this->metadata = map;
180 }
181
182 /**
183 * @return ALL of the metadata in map.
184 */
185 std::map<AString, AString>
186 MetaData::getAsMap() const
187 {
188 return this->metadata;
189 }
190
191
192 /**
193 * Remove a metadata entry.
194 *
195 * @param name Name of metadata entry that is to be removed.
196 *
197 */
198 void
199 MetaData::remove(const AString& name)
200 {
201 this->metadata.erase(name);
202 }
203
204 /**
205 * See if a metadata entry "name" exists.
206 *
207 * @param name Name of metadata entry.
208 * @return Returns true if the metadata entry "name" exists, else false.
209 *
210 */
211 bool
212 MetaData::exists(const AString& name) const
213 {
214 if (this->metadata.find(name) != this->metadata.end()) {
215 return true;
216 }
217 return false;
218 }
219
220 /**
221 * Get a value for metadata entry.
222 *
223 * @param name Name of metadata entry.
224 * @return The value of the metadata entry "name". If the
225 * metadata entry "name" does not exist an empty
226 * string is returned.
227 *
228 */
229 AString
230 MetaData::get(const AString& name) const
231 {
232 MetaDataConstIterator iter = this->metadata.find(name);
233 if (iter != this->metadata.end()) {
234 return iter->second;
235 }
236 return "";
237 }
238
239 /**
240 * Get the metadata as an integer value. If the metadata does not exist
241 * or its string representation is not a number, zero is returned.
242 * @param name - name of metadata.
243 * @param ok - is set to false if key not found, or value not integer
244 * @return Integer value associated with the metadata.
245 *
246 */
247 int32_t
248 MetaData::getInt(const AString& name, bool& ok) const
249 {
250 ok = false;
251 AString s = this->get(name);
252 if (s.length() > 0) {
253 int32_t i = AString_toInt(s, ok);
254 return i;
255 }
256 return 0;
257 }
258
259 /**
260 * Get the metadata as an float value. If the metadata does not exist
261 * or its string representation is not a number, zero is returned.
262 * @param name - name of metadata.
263 * @param ok - is set to false if key not found, or value not numeric
264 * @return Float value associated with the metadata.
265 *
266 */
267 float
268 MetaData::getFloat(const AString& name, bool& ok) const
269 {
270 ok = false;
271 AString s = this->get(name);
272 if (s.length() > 0) {
273 float f = AString_toFloat(s, ok);
274 return f;
275 }
276 return 0.0f;
277 }
278
279 /**
280 * Get names of all metadata.
281 *
282 * @return List of all metadata names.
283 *
284 */
285 std::vector<AString>
286 MetaData::getAllMetaDataNames() const
287 {
288 std::vector<AString> names;
289
290 for (MetaDataConstIterator iter = this->metadata.begin();
291 iter != this->metadata.end();
292 iter++) {
293 names.push_back(iter->first);
294 }
295 return names;
296 }
297
298 /**
299 * Replace a metadata name.
300 * @param oldName - old name of metadata.
301 * @param newName - new name of metadata.
302 *
303 */
304 void
305 MetaData::replaceName(
306 const AString& oldName,
307 const AString& newName)
308 {
309 MetaDataIterator iter = this->metadata.find(oldName);
310 if (iter != this->metadata.end()) {
311 AString value = iter->second;
312 this->remove(oldName);
313 this->set(newName, value);
314 }
315 }
316
317 void MetaData::writeCiftiXML1(XmlWriter& xmlWriter) const
318 {
319 if (metadata.empty()) return;//don't write an empty tag if we have no metadata
320 xmlWriter.writeStartElement("MetaData");
321 for (MetaDataConstIterator iter = metadata.begin(); iter != metadata.end(); ++iter)
322 {
323 xmlWriter.writeStartElement("MD");
324 xmlWriter.writeTextElement("Name", iter->first);
325 xmlWriter.writeTextElement("Value", iter->second);
326 xmlWriter.writeEndElement();
327 }
328 xmlWriter.writeEndElement();
329 }
330
331 void MetaData::writeCiftiXML2(XmlWriter& xmlWriter) const
332 {
333 writeCiftiXML1(xmlWriter);
334 }
335
336 void MetaData::readCiftiXML1(XmlReader& xml)
337 {
338 clear();
339 #ifdef CIFTILIB_USE_QT
340 while (!xml.atEnd())//don't check the current element's name
341 {
342 xml.readNext();
343 if (xml.isStartElement())
344 {
345 QStringRef name = xml.name();
346 if (name == "MD")
347 {
348 readEntry(xml);
349 } else {
350 throw CiftiException("unexpected tag name in MetaData: " + name.toString());
351 }
352 } else if (xml.isEndElement()) {
353 break;
354 }
355 }
356 #else
357 #ifdef CIFTILIB_USE_XMLPP
358 bool done = xml.is_empty_element();//NOTE: because libxml++ will NOT give a separate close element for <MetaData/>!!!
359 while (!done && xml.read())//false means no node was available to read, it will throw on malformed xml
360 {
361 switch (xml.get_node_type())
362 {
363 case XmlReader::Element:
364 {
365 AString name = xml.get_local_name();
366 if (name == "MD")
367 {
368 readEntry(xml);
369 } else {
370 throw CiftiException("unexpected tag name in MetaData: " + name);
371 }
372 break;
373 }
374 case XmlReader::EndElement:
375 done = true;
376 break;
377 default:
378 break;
379 }
380 }
381 #else
382 #error "not implemented"
383 #endif
384 #endif
385 CiftiAssert(XmlReader_checkEndElement(xml, "MetaData"));
386 }
387
388 void MetaData::readCiftiXML2(XmlReader& xml)
389 {
390 readCiftiXML1(xml);
391 }
392
393 void MetaData::readEntry(XmlReader& xml)
394 {
395 AString key, value;
396 bool haveKey = false, haveValue = false;
397 #ifdef CIFTILIB_USE_QT
398 while (!xml.atEnd())//don't check the current element's name
399 {
400 xml.readNext();
401 if (xml.isStartElement())
402 {
403 QStringRef name = xml.name();
404 if (name == "Name")
405 {
406 if (haveKey) throw CiftiException("MD element has multiple Name elements");
407 key = xml.readElementText();
408 haveKey = true;
409 } else if (name == "Value") {
410 if (haveValue) throw CiftiException("MD element has multiple Value elements");
411 value = xml.readElementText();
412 haveValue = true;
413 } else {
414 throw CiftiException("unexpected element name in MD: " + name.toString());
415 }
416 } else if (xml.isEndElement()) {
417 break;
418 }
419 }
420 #else
421 #ifdef CIFTILIB_USE_XMLPP
422 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
423 while (!done && xml.read())
424 {
425 switch (xml.get_node_type())
426 {
427 case XmlReader::Element:
428 {
429 AString name = xml.get_local_name();
430 if (name == "Name")
431 {
432 if (haveKey) throw CiftiException("MD element has multiple Name elements");
433 key = XmlReader_readElementText(xml);
434 haveKey = true;
435 } else if (name == "Value") {
436 if (haveValue) throw CiftiException("MD element has multiple Value elements");
437 name = XmlReader_readElementText(xml);
438 haveValue = true;
439 } else {
440 throw CiftiException("unexpected element name in MD: " + name);
441 }
442 break;
443 }
444 case XmlReader::EndElement:
445 done = true;
446 break;
447 default:
448 break;
449 }
450 }
451 #else
452 #error "not implemented"
453 #endif
454 #endif
455 if (haveKey && haveValue)
456 {
457 if (exists(key))
458 {
459 throw CiftiException("key '" + key + "' used more than once in MetaData");
460 } else {
461 set(key, value);
462 }
463 } else {
464 if (haveKey)
465 {
466 throw CiftiException("MD element has no Value element");
467 } else {
468 if (haveValue)
469 {
470 throw CiftiException("MD element has no Name element");
471 } else {
472 throw CiftiException("MD element has no Name or Value element");
473 }
474 }
475 }
476 CiftiAssert(XmlReader_checkEndElement(xml, "MD"));
477 }
0 #ifndef __METADATA_H__
1 #define __METADATA_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30
31 #include "Common/AString.h"
32
33 #include "Common/XmlAdapter.h"
34
35 #include <stdint.h>
36
37 #include <map>
38 #include <stdexcept>
39 #include <vector>
40
41 namespace cifti {
42
43 class MetaData {
44
45 public:
46 MetaData();
47
48 public:
49 MetaData(const MetaData& o);
50
51 MetaData& operator=(const MetaData& o);
52
53 bool operator==(const MetaData& rhs) const;
54
55 bool operator!=(const MetaData& rhs) const { return !((*this) == rhs); }
56
57 virtual ~MetaData();
58
59 private:
60 void copyHelper(const MetaData& o);
61
62 void initializeMembersMetaData();
63
64 public:
65 void clear();
66
67 void append(const MetaData& smd);
68
69 void replace(const MetaData& smd);
70
71 void set(const AString& name, const AString& value);
72
73 void setInt(const AString& name, const int32_t value);
74
75 void setFloat(const AString& name, const float value);
76
77 void replaceWithMap(const std::map<AString, AString>& map);
78
79 std::map<AString, AString> getAsMap() const;
80
81 void remove(const AString& name);
82
83 bool exists(const AString& name) const;
84
85 AString get(const AString& name) const;
86
87 int32_t getInt(const AString& name, bool& ok) const;
88
89 float getFloat(const AString& name, bool& ok) const;
90
91 std::vector<AString> getAllMetaDataNames() const;
92
93 void writeCiftiXML1(XmlWriter& xmlWriter) const;
94 void writeCiftiXML2(XmlWriter& xmlWriter) const;//for style, and in case it changes
95 void readCiftiXML1(XmlReader& xml);
96 void readCiftiXML2(XmlReader& xml);
97
98 private:
99 void readEntry(XmlReader& xml);
100
101 void replaceName(const AString& oldName,
102 const AString& newName);
103
104 public:
105
106 private:
107 /**the metadata storage. */
108 std::map<AString, AString> metadata;
109 typedef std::map<AString, AString>::iterator MetaDataIterator;
110 typedef std::map<AString, AString>::const_iterator MetaDataConstIterator;
111
112 };
113
114 } // namespace
115
116 #endif // __METADATA_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "StructureEnum.h"
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/CiftiException.h"
31
32 using namespace cifti;
33
34 std::vector<StructureEnum> StructureEnum::enumData;
35 bool StructureEnum::initializedFlag = false;
36
37 /**
38 * Constructor.
39 *
40 * @param enumValue
41 * An enumerated value.
42 * @param integerCode
43 * Integer code for this enumerated value.
44 *
45 * @param name
46 * Name of enumerated value.
47 *
48 * @param guiName
49 * User-friendly name for use in user-interface.
50 */
51 StructureEnum::StructureEnum(const Enum enumValue,
52 const AString& name,
53 const AString& guiName)
54 {
55 this->enumValue = enumValue;
56 this->name = name;
57 this->guiName = guiName;
58 }
59
60 /**
61 * Destructor.
62 */
63 StructureEnum::~StructureEnum()
64 {
65 }
66
67 /**
68 * Initialize the enumerated metadata.
69 */
70 void StructureEnum::initialize()
71 {
72 if (initializedFlag) {
73 return;
74 }
75 initializedFlag = true;
76
77 enumData.push_back(StructureEnum(CORTEX_LEFT,
78 "CORTEX_LEFT",
79 "CortexLeft"));
80
81 enumData.push_back(StructureEnum(CORTEX_RIGHT,
82 "CORTEX_RIGHT",
83 "CortexRight"));
84
85 enumData.push_back(StructureEnum(CEREBELLUM,
86 "CEREBELLUM",
87 "Cerebellum"));
88
89 enumData.push_back(StructureEnum(ACCUMBENS_LEFT,
90 "ACCUMBENS_LEFT",
91 "AccumbensLeft"));
92
93 enumData.push_back(StructureEnum(ACCUMBENS_RIGHT,
94 "ACCUMBENS_RIGHT",
95 "AccumbensRight"));
96
97 enumData.push_back(StructureEnum(ALL,
98 "ALL",
99 "All"));
100
101 enumData.push_back(StructureEnum(ALL_GREY_MATTER,
102 "ALL_GREY_MATTER",
103 "AllGreyMatter"));
104
105 enumData.push_back(StructureEnum(ALL_WHITE_MATTER,
106 "ALL_WHITE_MATTER",
107 "AllWhiteMatter"));
108
109 enumData.push_back(StructureEnum(AMYGDALA_LEFT,
110 "AMYGDALA_LEFT",
111 "AmygdalaLeft"));
112
113 enumData.push_back(StructureEnum(AMYGDALA_RIGHT,
114 "AMYGDALA_RIGHT",
115 "AmygdalaRight"));
116
117 enumData.push_back(StructureEnum(BRAIN_STEM,
118 "BRAIN_STEM",
119 "BrainStem"));
120
121 enumData.push_back(StructureEnum(CAUDATE_LEFT,
122 "CAUDATE_LEFT",
123 "CaudateLeft"));
124
125 enumData.push_back(StructureEnum(CAUDATE_RIGHT,
126 "CAUDATE_RIGHT",
127 "CaudateRight"));
128
129 enumData.push_back(StructureEnum(CEREBELLAR_WHITE_MATTER_LEFT,
130 "CEREBELLAR_WHITE_MATTER_LEFT",
131 "CerebellarWhiteMatterLeft"));
132
133 enumData.push_back(StructureEnum(CEREBELLAR_WHITE_MATTER_RIGHT,
134 "CEREBELLAR_WHITE_MATTER_RIGHT",
135 "CerebellarWhiteMatterRight"));
136
137 enumData.push_back(StructureEnum(CEREBELLUM_LEFT,
138 "CEREBELLUM_LEFT",
139 "CerebellumLeft"));
140
141 enumData.push_back(StructureEnum(CEREBELLUM_RIGHT,
142 "CEREBELLUM_RIGHT",
143 "CerebellumRight"));
144
145 enumData.push_back(StructureEnum(CEREBRAL_WHITE_MATTER_LEFT,
146 "CEREBRAL_WHITE_MATTER_LEFT",
147 "CerebralWhiteMatterLeft"));
148
149 enumData.push_back(StructureEnum(CEREBRAL_WHITE_MATTER_RIGHT,
150 "CEREBRAL_WHITE_MATTER_RIGHT",
151 "CerebralWhiteMatterRight"));
152
153 enumData.push_back(StructureEnum(CORTEX,
154 "CORTEX",
155 "Cortex"));
156
157 enumData.push_back(StructureEnum(DIENCEPHALON_VENTRAL_LEFT,
158 "DIENCEPHALON_VENTRAL_LEFT",
159 "DiencephalonVentralLeft"));
160
161 enumData.push_back(StructureEnum(DIENCEPHALON_VENTRAL_RIGHT,
162 "DIENCEPHALON_VENTRAL_RIGHT",
163 "DiencephalonVentralRight"));
164
165 enumData.push_back(StructureEnum(HIPPOCAMPUS_LEFT,
166 "HIPPOCAMPUS_LEFT",
167 "HippocampusLeft"));
168
169 enumData.push_back(StructureEnum(HIPPOCAMPUS_RIGHT,
170 "HIPPOCAMPUS_RIGHT",
171 "HippocampusRight"));
172
173 enumData.push_back(StructureEnum(INVALID,
174 "INVALID",
175 "Invalid"));
176
177 enumData.push_back(StructureEnum(OTHER,
178 "OTHER",
179 "Other"));
180
181 enumData.push_back(StructureEnum(OTHER_GREY_MATTER,
182 "OTHER_GREY_MATTER",
183 "OtherGreyMatter"));
184
185 enumData.push_back(StructureEnum(OTHER_WHITE_MATTER,
186 "OTHER_WHITE_MATTER",
187 "OtherWhiteMatter"));
188
189 enumData.push_back(StructureEnum(PALLIDUM_LEFT,
190 "PALLIDUM_LEFT",
191 "PallidumLeft"));
192
193 enumData.push_back(StructureEnum(PALLIDUM_RIGHT,
194 "PALLIDUM_RIGHT",
195 "PallidumRight"));
196
197 enumData.push_back(StructureEnum(PUTAMEN_LEFT,
198 "PUTAMEN_LEFT",
199 "PutamenLeft"));
200
201 enumData.push_back(StructureEnum(PUTAMEN_RIGHT,
202 "PUTAMEN_RIGHT",
203 "PutamenRight"));
204
205 enumData.push_back(StructureEnum(THALAMUS_LEFT,
206 "THALAMUS_LEFT",
207 "ThalamusLeft"));
208
209 enumData.push_back(StructureEnum(THALAMUS_RIGHT,
210 "THALAMUS_RIGHT",
211 "ThalamusRight"));
212 }
213
214 /**
215 * Find the data for and enumerated value.
216 * @param enumValue
217 * The enumerated value.
218 * @return Pointer to data for this enumerated type
219 * or NULL if no data for type or if type is invalid.
220 */
221 const StructureEnum* StructureEnum::findData(const Enum enumValue)
222 {
223 if (initializedFlag == false) initialize();
224
225 size_t num = enumData.size();
226 for (size_t i = 0; i < num; i++) {
227 const StructureEnum* d = &enumData[i];
228 if (d->enumValue == enumValue) {
229 return d;
230 }
231 }
232
233 throw CiftiException("unable to find enumeration value " + AString_number(enumValue));
234 }
235
236 /**
237 * Get a string representation of the enumerated type.
238 * @param enumValue
239 * Enumerated value.
240 * @return
241 * String representing enumerated value.
242 */
243 AString StructureEnum::toName(Enum enumValue) {
244 if (initializedFlag == false) initialize();
245
246 const StructureEnum* enumInstance = findData(enumValue);
247 return enumInstance->name;
248 }
249
250 /**
251 * Get an enumerated value corresponding to its name.
252 * @param name
253 * Name of enumerated value.
254 * @param isValidOut
255 * If not NULL, it is set indicating that a
256 * enum value exists for the input name.
257 * @return
258 * Enumerated value.
259 */
260 StructureEnum::Enum StructureEnum::fromName(const AString& name, bool* isValidOut)
261 {
262 if (initializedFlag == false) initialize();
263
264 bool validFlag = false;
265 Enum enumValue = INVALID;
266
267 for (std::vector<StructureEnum>::iterator iter = enumData.begin();
268 iter != enumData.end();
269 iter++) {
270 const StructureEnum& d = *iter;
271 if (d.name == name) {
272 enumValue = d.enumValue;
273 validFlag = true;
274 break;
275 }
276 }
277
278 if (isValidOut != 0) {
279 *isValidOut = validFlag;
280 }
281 else if (validFlag == false) {
282 throw CiftiException("Name " + name + "failed to match enumerated value for type StructureEnum");
283 }
284 return enumValue;
285 }
286
287 /**
288 * Get a GUI string representation of the enumerated type.
289 * @param enumValue
290 * Enumerated value.
291 * @return
292 * String representing enumerated value.
293 */
294 AString StructureEnum::toGuiName(Enum enumValue) {
295 if (initializedFlag == false) initialize();
296
297 const StructureEnum* enumInstance = findData(enumValue);
298 return enumInstance->guiName;
299 }
300
301 /**
302 * Get an enumerated value corresponding to its GUI name.
303 * @param guiName
304 * Name of enumerated value.
305 * @param isValidOut
306 * If not NULL, it is set indicating that a
307 * enum value exists for the input name.
308 * @return
309 * Enumerated value.
310 */
311 StructureEnum::Enum StructureEnum::fromGuiName(const AString& guiName, bool* isValidOut)
312 {
313 if (initializedFlag == false) initialize();
314
315 bool validFlag = false;
316 Enum enumValue = INVALID;
317
318 for (std::vector<StructureEnum>::iterator iter = enumData.begin();
319 iter != enumData.end();
320 iter++) {
321 const StructureEnum& d = *iter;
322 if (d.guiName == guiName) {
323 enumValue = d.enumValue;
324 validFlag = true;
325 break;
326 }
327 }
328
329 if (isValidOut != 0) {
330 *isValidOut = validFlag;
331 }
332 else if (validFlag == false) {
333 throw CiftiException("guiName " + guiName + "failed to match enumerated value for type StructureEnum");
334 }
335 return enumValue;
336 }
337
338 /**
339 * Get a GUI string representation of the enumerated type.
340 * @param enumValue
341 * Enumerated value.
342 * @return
343 * String representing enumerated value.
344 */
345 AString StructureEnum::toCiftiName(Enum enumValue) {
346 if (initializedFlag == false) initialize();
347
348 const StructureEnum* enumInstance = findData(enumValue);
349 return "CIFTI_STRUCTURE_" + enumInstance->name;
350 }
351
352 /**
353 * Get an enumerated value corresponding to its GUI name.
354 * @param ciftiName
355 * Name of enumerated value.
356 * @param isValidOut
357 * If not NULL, it is set indicating that a
358 * enum value exists for the input name.
359 * @return
360 * Enumerated value.
361 */
362 StructureEnum::Enum StructureEnum::fromCiftiName(const AString& ciftiName, bool* isValidOut)
363 {
364 if (initializedFlag == false) initialize();
365
366 bool validFlag = false;
367 Enum enumValue = INVALID;
368 if (AString_substr(ciftiName, 0, 16) == "CIFTI_STRUCTURE_")
369 {
370 AString toMatch = AString_substr(ciftiName, 16);
371 for (std::vector<StructureEnum>::iterator iter = enumData.begin();
372 iter != enumData.end();
373 iter++) {
374 const StructureEnum& d = *iter;
375 if (toMatch == d.name) {
376 enumValue = d.enumValue;
377 validFlag = true;
378 break;
379 }
380 }
381 }
382
383 if (isValidOut != 0) {
384 *isValidOut = validFlag;
385 }
386 else if (validFlag == false) {
387 throw CiftiException("ciftiName " + ciftiName + "failed to match enumerated value for type StructureEnum");
388 }
389 return enumValue;
390 }
391
392 /**
393 * Get all of the enumerated type values. The values can be used
394 * as parameters to toXXX() methods to get associated metadata.
395 *
396 * @param allEnums
397 * A vector that is OUTPUT containing all of the enumerated values
398 * except ALL.
399 */
400 void StructureEnum::getAllEnums(std::vector<StructureEnum::Enum>& allEnums)
401 {
402 if (initializedFlag == false) initialize();
403
404 allEnums.clear();
405
406 for (std::vector<StructureEnum>::iterator iter = enumData.begin();
407 iter != enumData.end();
408 iter++) {
409 StructureEnum::Enum value =iter->enumValue;
410 if (value == ALL) {
411 // nothing
412 }
413 else {
414 allEnums.push_back(iter->enumValue);
415 }
416 }
417 }
418
419 /**
420 * Is this 'right' structure?
421 * @param enumValue
422 * The enumerated type.
423 * @return
424 * true if the enumerated value represents a 'right' structure, else false.
425 */
426 bool StructureEnum::isRight(const Enum enumValue)
427 {
428 switch (enumValue)
429 {
430 case ACCUMBENS_RIGHT:
431 case AMYGDALA_RIGHT:
432 case CAUDATE_RIGHT:
433 case CEREBELLAR_WHITE_MATTER_RIGHT:
434 case CEREBELLUM_RIGHT:
435 case CEREBRAL_WHITE_MATTER_RIGHT:
436 case CORTEX_RIGHT:
437 case DIENCEPHALON_VENTRAL_RIGHT:
438 case HIPPOCAMPUS_RIGHT:
439 case PALLIDUM_RIGHT:
440 case PUTAMEN_RIGHT:
441 case THALAMUS_RIGHT:
442 return true;
443 case ALL://avoid default so smart compilers can warn when a new structure isn't added here
444 case ALL_GREY_MATTER:
445 case ALL_WHITE_MATTER:
446 case BRAIN_STEM:
447 case CEREBELLUM:
448 case CORTEX:
449 case INVALID:
450 case OTHER:
451 case OTHER_GREY_MATTER:
452 case OTHER_WHITE_MATTER:
453 return false;//visually separate none/both from opposite cases
454 case ACCUMBENS_LEFT:
455 case AMYGDALA_LEFT:
456 case CAUDATE_LEFT:
457 case CEREBELLAR_WHITE_MATTER_LEFT:
458 case CEREBELLUM_LEFT:
459 case CEREBRAL_WHITE_MATTER_LEFT:
460 case CORTEX_LEFT:
461 case DIENCEPHALON_VENTRAL_LEFT:
462 case HIPPOCAMPUS_LEFT:
463 case PALLIDUM_LEFT:
464 case PUTAMEN_LEFT:
465 case THALAMUS_LEFT:
466 return false;
467 }
468 CiftiAssert(false);
469 return false;
470 }
471
472 /**
473 * Is this 'left' structure?
474 * @param enumValue
475 * The enumerated type.
476 * @return
477 * true if the enumerated value represents a 'left' structure, else false.
478 */
479 bool StructureEnum::isLeft(const Enum enumValue)
480 {
481 switch (enumValue)
482 {
483 case ACCUMBENS_LEFT:
484 case AMYGDALA_LEFT:
485 case CAUDATE_LEFT:
486 case CEREBELLAR_WHITE_MATTER_LEFT:
487 case CEREBELLUM_LEFT:
488 case CEREBRAL_WHITE_MATTER_LEFT:
489 case CORTEX_LEFT:
490 case DIENCEPHALON_VENTRAL_LEFT:
491 case HIPPOCAMPUS_LEFT:
492 case PALLIDUM_LEFT:
493 case PUTAMEN_LEFT:
494 case THALAMUS_LEFT:
495 return true;
496 case ALL://avoid default so smart compilers can warn when a new structure isn't added here
497 case ALL_GREY_MATTER:
498 case ALL_WHITE_MATTER:
499 case BRAIN_STEM:
500 case CEREBELLUM:
501 case CORTEX:
502 case INVALID:
503 case OTHER:
504 case OTHER_GREY_MATTER:
505 case OTHER_WHITE_MATTER:
506 return false;//visually separate none/both from opposite cases
507 case ACCUMBENS_RIGHT:
508 case AMYGDALA_RIGHT:
509 case CAUDATE_RIGHT:
510 case CEREBELLAR_WHITE_MATTER_RIGHT:
511 case CEREBELLUM_RIGHT:
512 case CEREBRAL_WHITE_MATTER_RIGHT:
513 case CORTEX_RIGHT:
514 case DIENCEPHALON_VENTRAL_RIGHT:
515 case HIPPOCAMPUS_RIGHT:
516 case PALLIDUM_RIGHT:
517 case PUTAMEN_RIGHT:
518 case THALAMUS_RIGHT:
519 return false;
520 }
521 CiftiAssert(false);
522 return false;
523 }
524
525 /**
526 * Are the two structure's cortices and contralateral (is one CortexLeft
527 * and one CortexRight)?
528 *
529 * @param enumValueA
530 * First structure enumerated type.
531 * @param enumValueB
532 * Second structure enumerated type.
533 * @return
534 * True if one is CORTEX_LEFT and one is CORTEX_LEFT.
535 */
536 bool StructureEnum::isCortexContralateral(const Enum enumValueA,
537 const Enum enumValueB)
538 {
539 if ((enumValueA == CORTEX_LEFT)
540 && (enumValueB == CORTEX_RIGHT)) {
541 return true;
542 }
543 if ((enumValueA == CORTEX_RIGHT)
544 && (enumValueB == CORTEX_LEFT)) {
545 return true;
546 }
547
548 return false;
549 }
550
551 /**
552 * For the given structure return its contralateral structure.
553 * Thats is, if this is a left/right structure return its
554 * corresponding structure from the other side.
555 *
556 * @param enumValue
557 * Structure for which contralateral structure is desired.
558 * @return The contralateral structure or NULL if it does
559 * not have a contralateral structure.
560 */
561 StructureEnum::Enum StructureEnum::getContralateralStructure(const Enum enumValue)
562 {
563 StructureEnum::Enum contralateralStructure = INVALID;
564
565 switch (enumValue) {
566 case ACCUMBENS_LEFT:
567 contralateralStructure = ACCUMBENS_RIGHT;
568 break;
569 case ACCUMBENS_RIGHT:
570 contralateralStructure = ACCUMBENS_LEFT;
571 break;
572 case ALL:
573 contralateralStructure = INVALID;
574 break;
575 case ALL_GREY_MATTER:
576 break;
577 case ALL_WHITE_MATTER:
578 break;
579 case AMYGDALA_LEFT:
580 contralateralStructure = AMYGDALA_RIGHT;
581 break;
582 case AMYGDALA_RIGHT:
583 contralateralStructure = AMYGDALA_LEFT;
584 break;
585 case BRAIN_STEM:
586 contralateralStructure = INVALID;
587 break;
588 case CAUDATE_LEFT:
589 contralateralStructure = CAUDATE_RIGHT;
590 break;
591 case CAUDATE_RIGHT:
592 contralateralStructure = CAUDATE_LEFT;
593 break;
594 case CEREBELLAR_WHITE_MATTER_LEFT:
595 contralateralStructure= CEREBELLAR_WHITE_MATTER_RIGHT;
596 break;
597 case CEREBELLAR_WHITE_MATTER_RIGHT:
598 contralateralStructure = CEREBELLAR_WHITE_MATTER_LEFT;
599 break;
600 case CEREBELLUM:
601 contralateralStructure = INVALID;
602 break;
603 case CEREBELLUM_LEFT:
604 contralateralStructure = CEREBELLUM_RIGHT;
605 break;
606 case CEREBELLUM_RIGHT:
607 contralateralStructure = CEREBELLUM_LEFT;
608 break;
609 case CEREBRAL_WHITE_MATTER_LEFT:
610 contralateralStructure = CEREBELLAR_WHITE_MATTER_RIGHT;
611 break;
612 case CEREBRAL_WHITE_MATTER_RIGHT:
613 contralateralStructure = CEREBELLAR_WHITE_MATTER_LEFT;
614 break;
615 case CORTEX:
616 break;
617 case CORTEX_LEFT:
618 contralateralStructure = CORTEX_RIGHT;
619 break;
620 case CORTEX_RIGHT:
621 contralateralStructure = CORTEX_LEFT;
622 break;
623 case DIENCEPHALON_VENTRAL_LEFT:
624 contralateralStructure = DIENCEPHALON_VENTRAL_RIGHT;
625 break;
626 case DIENCEPHALON_VENTRAL_RIGHT:
627 contralateralStructure = DIENCEPHALON_VENTRAL_LEFT;
628 break;
629 case HIPPOCAMPUS_LEFT:
630 contralateralStructure = HIPPOCAMPUS_RIGHT;
631 break;
632 case HIPPOCAMPUS_RIGHT:
633 contralateralStructure = HIPPOCAMPUS_LEFT;
634 break;
635 case INVALID:
636 contralateralStructure = INVALID;
637 break;
638 case PALLIDUM_LEFT:
639 contralateralStructure = PALLIDUM_RIGHT;
640 break;
641 case PALLIDUM_RIGHT:
642 contralateralStructure = PALLIDUM_LEFT;
643 break;
644 case OTHER:
645 contralateralStructure = INVALID;
646 break;
647 case OTHER_GREY_MATTER:
648 break;
649 case OTHER_WHITE_MATTER:
650 break;
651 case PUTAMEN_LEFT:
652 contralateralStructure = PUTAMEN_RIGHT;
653 break;
654 case PUTAMEN_RIGHT:
655 contralateralStructure = PUTAMEN_LEFT;
656 break;
657 case THALAMUS_LEFT:
658 contralateralStructure = THALAMUS_RIGHT;
659 break;
660 case THALAMUS_RIGHT:
661 contralateralStructure = THALAMUS_LEFT;
662 break;
663 }
664
665 return contralateralStructure;
666 }
667
668
0 #ifndef __STRUCTURE_ENUM__H_
1 #define __STRUCTURE_ENUM__H_
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/AString.h"
31
32 #include <stdint.h>
33 #include <vector>
34
35 namespace cifti {
36
37 /**
38 * \brief Enumerated type for a structure in a brain.
39 *
40 * Enumerated types for the individual structures in a brain.
41 */
42 class StructureEnum {
43
44 public:
45 /**
46 * Enumerated values.
47 */
48 enum Enum {
49 /** Invalid */
50 INVALID,
51 /** All Strucures */
52 ALL,
53 /** All white matter */
54 ALL_WHITE_MATTER,
55 /** All grey matter */
56 ALL_GREY_MATTER,
57 /** Left Nucleus Accumbens */
58 ACCUMBENS_LEFT,
59 /** Right Nucleus Accumbens */
60 ACCUMBENS_RIGHT,
61 /** Left Amygdala */
62 AMYGDALA_LEFT,
63 /** Right Amygdala */
64 AMYGDALA_RIGHT,
65 /** Brain Stem */
66 BRAIN_STEM,
67 /** Left Caudate */
68 CAUDATE_LEFT,
69 /** Right Caudate */
70 CAUDATE_RIGHT,
71 /** Cerebellar white matter left */
72 CEREBELLAR_WHITE_MATTER_LEFT,
73 /** Cerebellar white matter right */
74 CEREBELLAR_WHITE_MATTER_RIGHT,
75 /** Cerebellum */
76 CEREBELLUM,
77 /** Left Cerebellum */
78 CEREBELLUM_LEFT,
79 /** Right Cerebellum */
80 CEREBELLUM_RIGHT,
81 /** Cerebral white matter left */
82 CEREBRAL_WHITE_MATTER_LEFT,
83 /** Cerebral white matter right */
84 CEREBRAL_WHITE_MATTER_RIGHT,
85 /** Cortex not specified */
86 CORTEX,
87 /** Left Cerebral Cortex */
88 CORTEX_LEFT,
89 /** Right Cerebral Cortex*/
90 CORTEX_RIGHT,
91 /** Left Ventral Diencephalon */
92 DIENCEPHALON_VENTRAL_LEFT,
93 /** Right Ventral Diencephalon */
94 DIENCEPHALON_VENTRAL_RIGHT,
95 /** Left Hippocampus */
96 HIPPOCAMPUS_LEFT,
97 /** Right Hippocampus */
98 HIPPOCAMPUS_RIGHT,
99 /** Left Pallidum */
100 PALLIDUM_LEFT,
101 /** Right Pallidum */
102 PALLIDUM_RIGHT,
103 /** Other structure not specified */
104 OTHER,
105 /** Other grey matter */
106 OTHER_GREY_MATTER,
107 /** Other white matter */
108 OTHER_WHITE_MATTER,
109 /** Left Putamen */
110 PUTAMEN_LEFT,
111 /** Right Putamen */
112 PUTAMEN_RIGHT,
113 /** Left Thalamus */
114 THALAMUS_LEFT,
115 /** Right Thalamus */
116 THALAMUS_RIGHT
117 };
118
119
120 ~StructureEnum();
121
122 static AString toName(Enum enumValue);
123
124 static Enum fromName(const AString& name, bool* isValidOut);
125
126 static AString toGuiName(Enum enumValue);
127
128 static Enum fromGuiName(const AString& guiName, bool* isValidOut);
129
130 static AString toCiftiName(Enum enumValue);
131
132 static Enum fromCiftiName(const AString& ciftiName, bool* isValidOut);
133
134 static void getAllEnums(std::vector<Enum>& allEnums);
135
136 static bool isRight(const Enum enumValue);
137
138 static bool isLeft(const Enum enumValue);
139
140 static bool isCortexContralateral(const Enum enumValueA,
141 const Enum enumValueB);
142
143 static Enum getContralateralStructure(const Enum enumValue);
144
145 private:
146 StructureEnum(const Enum enumValue,
147 const AString& name,
148 const AString& guiName);
149
150 static const StructureEnum* findData(const Enum enumValue);
151
152 /** Holds all instance of enum values and associated metadata */
153 static std::vector<StructureEnum> enumData;
154
155 /** Initialize instances that contain the enum values and metadata */
156 static void initialize();
157
158 /** Indicates instance of enum values and metadata have been initialized */
159 static bool initializedFlag;
160
161 /** The enumerated type value for an instance */
162 Enum enumValue;
163
164 /** The name, a text string that is identical to the enumerated value */
165 AString name;
166
167 /** A user-friendly name that is displayed in the GUI */
168 AString guiName;
169 };
170
171 } // namespace
172 #endif //__STRUCTURE_ENUM__H_
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "VolumeSpace.h"
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/CiftiException.h"
31 #include "Common/FloatMatrix.h"
32
33 #ifdef CIFTILIB_USE_QT
34 #include <QRegExp>
35 #include <QStringList>
36 #else
37 #ifdef CIFTILIB_USE_XMLPP
38 #else
39 #error "not implemented"
40 #endif
41 #endif
42
43 #include <algorithm>
44 #include <cmath>
45 #include <iostream>
46
47 using namespace std;
48 using namespace cifti;
49
50 VolumeSpace::VolumeSpace()
51 {
52 m_dims[0] = 0;
53 m_dims[1] = 0;
54 m_dims[2] = 0;
55 m_sform = FloatMatrix::identity(4).getMatrix();
56 computeInverse();
57 }
58
59 VolumeSpace::VolumeSpace(const int64_t dims[3], const vector<vector<float> >& sform)
60 {
61 setSpace(dims, sform);
62 }
63
64 VolumeSpace::VolumeSpace(const int64_t dims[3], const float sform[12])
65 {
66 setSpace(dims, sform);
67 }
68
69 void VolumeSpace::setSpace(const int64_t dims[3], const vector<vector<float> >& sform)
70 {
71 if (sform.size() < 2 || sform.size() > 4)
72 {
73 CiftiAssert(false);
74 throw CiftiException("VolumeSpace initialized with wrong size sform");
75 }
76 for (int i = 0; i < (int)sform.size(); ++i)
77 {
78 if (sform[i].size() != 4)
79 {
80 CiftiAssert(false);
81 throw CiftiException("VolumeSpace initialized with wrong size sform");
82 }
83 }
84 m_dims[0] = dims[0];
85 m_dims[1] = dims[1];
86 m_dims[2] = dims[2];
87 m_sform = sform;
88 m_sform.resize(4);//make sure its 4x4
89 m_sform[3].resize(4);
90 m_sform[3][0] = 0.0f;//force the fourth row to be correct
91 m_sform[3][1] = 0.0f;
92 m_sform[3][2] = 0.0f;
93 m_sform[3][3] = 1.0f;
94 computeInverse();
95 }
96
97 void VolumeSpace::setSpace(const int64_t dims[3], const float sform[12])
98 {
99 m_sform = FloatMatrix::identity(4).getMatrix();
100 for (int i = 0; i < 3; ++i)
101 {
102 for (int j = 0; j < 4; ++j)
103 {
104 m_sform[i][j] = sform[i * 4 + j];
105 }
106 }
107 m_dims[0] = dims[0];
108 m_dims[1] = dims[1];
109 m_dims[2] = dims[2];
110 computeInverse();
111 }
112
113 void VolumeSpace::computeInverse()
114 {
115 m_inverse = FloatMatrix(m_sform).inverse().getMatrix();
116 }
117
118 void VolumeSpace::spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float& indexOut1, float& indexOut2, float& indexOut3) const
119 {
120 indexOut1 = coordIn1 * m_inverse[0][0] + coordIn2 * m_inverse[0][1] + coordIn3 * m_inverse[0][2] + m_inverse[0][3];
121 indexOut2 = coordIn1 * m_inverse[1][0] + coordIn2 * m_inverse[1][1] + coordIn3 * m_inverse[1][2] + m_inverse[1][3];
122 indexOut3 = coordIn1 * m_inverse[2][0] + coordIn2 * m_inverse[2][1] + coordIn3 * m_inverse[2][2] + m_inverse[2][3];
123 }
124
125 void VolumeSpace::enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const
126 {
127 float tempInd1, tempInd2, tempInd3;
128 spaceToIndex(coordIn1, coordIn2, coordIn3, tempInd1, tempInd2, tempInd3);
129 indexOut1 = (int64_t)floor(0.5f + tempInd1);
130 indexOut2 = (int64_t)floor(0.5f + tempInd2);
131 indexOut3 = (int64_t)floor(0.5f + tempInd3);
132 }
133
134 bool VolumeSpace::matchesVolumeSpace(const VolumeSpace& right) const
135 {
136 for (int i = 0; i < 3; ++i)
137 {
138 if (m_dims[i] != right.m_dims[i])
139 {
140 return false;
141 }
142 }
143 const float TOLER_RATIO = 0.999f;//ratio a spacing element can mismatch by
144 for (int i = 0; i < 3; ++i)
145 {
146 for (int j = 0; j < 4; ++j)
147 {
148 float leftelem = m_sform[i][j];
149 float rightelem = right.m_sform[i][j];
150 if ((leftelem != rightelem) && (leftelem == 0.0f || rightelem == 0.0f || (leftelem / rightelem < TOLER_RATIO || rightelem / leftelem < TOLER_RATIO)))
151 {
152 return false;
153 }
154 }
155 }
156 return true;
157 }
158
159 bool VolumeSpace::operator==(const VolumeSpace& right) const
160 {
161 for (int i = 0; i < 3; ++i)
162 {
163 if (m_dims[i] != right.m_dims[i])
164 {
165 return false;
166 }
167 }
168 for (int i = 0; i < 3; ++i)
169 {
170 for (int j = 0; j < 4; ++j)
171 {
172 if (m_sform[i][j] != right.m_sform[i][j])
173 {
174 return false;
175 }
176 }
177 }
178 return true;
179 }
180
181 void VolumeSpace::getSpacingVectors(Vector3D& iStep, Vector3D& jStep, Vector3D& kStep, Vector3D& origin) const
182 {
183 FloatMatrix(m_sform).getAffineVectors(iStep, jStep, kStep, origin);
184 }
185
186 void VolumeSpace::getOrientAndSpacingForPlumb(OrientTypes* orientOut, float* spacingOut, float* originOut) const
187 {
188 CiftiAssert(isPlumb());
189 if (!isPlumb())
190 {
191 throw CiftiException("orientation and spacing asked for on non-plumb volume space");//this will fail MISERABLY on non-plumb volumes, so throw otherwise
192 }
193 for (int i = 0; i < 3; ++i)
194 {
195 for (int j = 0; j < 3; ++j)
196 {
197 if (m_sform[i][j] != 0.0f)
198 {
199 spacingOut[j] = m_sform[i][j];
200 originOut[j] = m_sform[i][3];
201 bool negative = (m_sform[i][j] < 0.0f);
202 switch (i)
203 {
204 case 0:
205 //left/right
206 orientOut[j] = (negative ? RIGHT_TO_LEFT : LEFT_TO_RIGHT);
207 break;
208 case 1:
209 //forward/back
210 orientOut[j] = (negative ? ANTERIOR_TO_POSTERIOR : POSTERIOR_TO_ANTERIOR);
211 break;
212 case 2:
213 //up/down
214 orientOut[j] = (negative ? SUPERIOR_TO_INFERIOR : INFERIOR_TO_SUPERIOR);
215 break;
216 default:
217 //will never get called
218 break;
219 };
220 }
221 }
222 }
223 }
224
225 void VolumeSpace::getOrientation(VolumeSpace::OrientTypes orientOut[3]) const
226 {
227 Vector3D ivec, jvec, kvec, origin;
228 getSpacingVectors(ivec, jvec, kvec, origin);
229 int next = 1, bestarray[3] = {0, 0, 0};
230 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
231 for (int first = 0; first < 3; ++first)//brute force search for best fit - only 6 to try
232 {
233 int third = 3 - first - next;
234 float testVal = abs(ivec[first] * jvec[next] * kvec[third]);
235 if (testVal > bestVal)
236 {
237 bestVal = testVal;
238 bestarray[0] = first;
239 bestarray[1] = next;
240 }
241 testVal = abs(ivec[first] * jvec[third] * kvec[next]);
242 if (testVal > bestVal)
243 {
244 bestVal = testVal;
245 bestarray[0] = first;
246 bestarray[1] = third;
247 }
248 next = 0;
249 }
250 bestarray[2] = 3 - bestarray[0] - bestarray[1];
251 Vector3D spaceHats[3];//to translate into enums without casting
252 spaceHats[0] = ivec;
253 spaceHats[1] = jvec;
254 spaceHats[2] = kvec;
255 for (int i = 0; i < 3; ++i)
256 {
257 bool neg = (spaceHats[i][bestarray[i]] < 0.0f);
258 switch (bestarray[i])
259 {
260 case 0:
261 if (neg)
262 {
263 orientOut[i] = RIGHT_TO_LEFT;
264 } else {
265 orientOut[i] = LEFT_TO_RIGHT;
266 }
267 break;
268 case 1:
269 if (neg)
270 {
271 orientOut[i] = ANTERIOR_TO_POSTERIOR;
272 } else {
273 orientOut[i] = POSTERIOR_TO_ANTERIOR;
274 }
275 break;
276 case 2:
277 if (neg)
278 {
279 orientOut[i] = SUPERIOR_TO_INFERIOR;
280 } else {
281 orientOut[i] = INFERIOR_TO_SUPERIOR;
282 }
283 break;
284 default:
285 CiftiAssert(0);
286 }
287 }
288 }
289
290 bool VolumeSpace::isPlumb() const
291 {
292 char axisUsed = 0;
293 char indexUsed = 0;
294 for (int i = 0; i < 3; ++i)
295 {
296 for (int j = 0; j < 3; ++j)
297 {
298 if (m_sform[i][j] != 0.0f)
299 {
300 if (axisUsed & (1<<i))
301 {
302 return false;
303 }
304 if (indexUsed & (1<<j))
305 {
306 return false;
307 }
308 axisUsed |= (1<<i);
309 indexUsed |= (1<<j);
310 }
311 }
312 }
313 return true;
314 }
315
316 void VolumeSpace::readCiftiXML1(XmlReader& xml)
317 {
318 vector<AString> mandAttrs(1, "VolumeDimensions"), transAttrs(3);
319 transAttrs[0] = "UnitsXYZ";
320 transAttrs[1] = "DataSpace";//we ignore the values in these, but they are required by cifti-1, so check that they exist
321 transAttrs[2] = "TransformedSpace";
322 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
323 vector<AString> dimStrings = AString_split(myAttrs.mandatoryVals[0], ',');
324 if (dimStrings.size() != 3)
325 {
326 throw CiftiException("VolumeDimensions attribute of Volume must contain exactly two commas");
327 }
328 int64_t newDims[3];//don't parse directly into the internal variables
329 bool ok = false;
330 for (int i = 0; i < 3; ++i)
331 {
332 newDims[i] = AString_toInt(dimStrings[i], ok);
333 if (!ok)
334 {
335 throw CiftiException("noninteger found in VolumeDimensions attribute of Volume: " + dimStrings[i]);
336 }
337 if (newDims[i] < 1)
338 {
339 throw CiftiException("found bad value in VolumeDimensions attribute of Volume: " + dimStrings[i]);
340 }
341 }
342 if (!XmlReader_readNextStartElement(xml))
343 {
344 throw CiftiException("failed to find TransformationMatrixVoxelIndicesIJKtoXYZ element in Volume");
345 }
346 if (XmlReader_elementName(xml) != "TransformationMatrixVoxelIndicesIJKtoXYZ")
347 {
348 throw CiftiException("unexpected element in Volume: " + XmlReader_elementName(xml));
349 }
350 XmlAttributesResult transAttrsRes = XmlReader_parseAttributes(xml, transAttrs);
351 float mult = 0.0f;
352 if (transAttrsRes.mandatoryVals[0] == "NIFTI_UNITS_MM")
353 {
354 mult = 1.0f;
355 } else if (transAttrsRes.mandatoryVals[0] == "NIFTI_UNITS_MICRON") {
356 mult = 0.001f;
357 } else {
358 throw CiftiException("unrecognized value for UnitsXYZ in TransformationMatrixVoxelIndicesIJKtoXYZ: " + transAttrsRes.mandatoryVals[0]);
359 }
360 AString accum = XmlReader_readElementText(xml);
361 #ifdef CIFTILIB_USE_QT
362 if (xml.hasError()) return;
363 #endif
364 vector<AString> matrixStrings = AString_split_whitespace(accum);
365 if (matrixStrings.size() != 16)
366 {
367 throw CiftiException("text content of TransformationMatrixVoxelIndicesIJKtoXYZ must have exactly 16 numbers separated by whitespace");
368 }
369 FloatMatrix newsform = FloatMatrix::zeros(4, 4);
370 for (int j = 0; j < 4; ++j)
371 {
372 for (int i = 0; i < 4; ++i)
373 {
374 newsform[j][i] = AString_toFloat(matrixStrings[i + j * 4], ok);
375 if (!ok)
376 {
377 throw CiftiException("non-number in text of TransformationMatrixVoxelIndicesIJKtoXYZ: " + matrixStrings[i + j * 4]);
378 }
379 }
380 }
381 if (newsform[3][0] != 0.0f || newsform[3][1] != 0.0f || newsform[3][2] != 0.0f || newsform[3][3] != 1.0f)//yes, using equals, because those are all exact in float
382 {
383 cerr << "last row of matrix in TransformationMatrixVoxelIndicesIJKtoXYZ is not 0 0 0 1" << endl;//not an exception, because some cifti-1 exist with this wrong
384 }
385 if (XmlReader_readNextStartElement(xml))//find Volume end element
386 {
387 throw CiftiException("unexpected element in Volume: " + XmlReader_elementName(xml));
388 }
389 newsform *= mult;//apply units
390 newsform[3][3] = 1.0f;//reset [3][3], since it isn't spatial
391 setSpace(newDims, newsform.getMatrix());
392 CiftiAssert(XmlReader_checkEndElement(xml, "Volume"));
393 }
394
395 void VolumeSpace::readCiftiXML2(XmlReader& xml)
396 {//we changed stuff, so separate code
397 vector<AString> mandAttrs(1, "VolumeDimensions"), transAttrs(1, "MeterExponent");
398 XmlAttributesResult myAttrs = XmlReader_parseAttributes(xml, mandAttrs);
399 vector<AString> dimStrings = AString_split(myAttrs.mandatoryVals[0], ',');
400 if (dimStrings.size() != 3)
401 {
402 throw CiftiException("VolumeDimensions attribute of Volume must contain exactly two commas");
403 }
404 int64_t newDims[3];//don't parse directly into the internal variables
405 bool ok = false;
406 for (int i = 0; i < 3; ++i)
407 {
408 newDims[i] = AString_toInt(dimStrings[i], ok);
409 if (!ok)
410 {
411 throw CiftiException("noninteger found in VolumeDimensions attribute of Volume: " + dimStrings[i]);
412 }
413 if (newDims[i] < 1)
414 {
415 throw CiftiException("found bad value in VolumeDimensions attribute of Volume: " + dimStrings[i]);
416 }
417 }
418 if (!XmlReader_readNextStartElement(xml))
419 {
420 throw CiftiException("failed to find TransformationMatrixVoxelIndicesIJKtoXYZ element in Volume");
421 }
422 if (XmlReader_elementName(xml) != "TransformationMatrixVoxelIndicesIJKtoXYZ")
423 {
424 throw CiftiException("unexpected element in Volume: " + XmlReader_elementName(xml));
425 }
426 XmlAttributesResult transAttrsRes = XmlReader_parseAttributes(xml, transAttrs);
427 int exponent = AString_toInt(transAttrsRes.mandatoryVals[0], ok);
428 if (!ok)
429 {
430 throw CiftiException("noninteger value for MeterExponent in TransformationMatrixVoxelIndicesIJKtoXYZ: " + transAttrsRes.mandatoryVals[0]);
431 }
432 float mult = pow(10.0f, exponent + 3);//because our internal units are mm
433 AString accum = XmlReader_readElementText(xml);
434 #ifdef CIFTILIB_USE_QT
435 if (xml.hasError()) return;
436 #endif
437 vector<AString> matrixStrings = AString_split_whitespace(accum);
438 if (matrixStrings.size() != 16)
439 {
440 throw CiftiException("text content of TransformationMatrixVoxelIndicesIJKtoXYZ must have exactly 16 numbers separated by whitespace");
441 }
442 FloatMatrix newsform = FloatMatrix::zeros(4, 4);
443 for (int j = 0; j < 4; ++j)
444 {
445 for (int i = 0; i < 4; ++i)
446 {
447 newsform[j][i] = AString_toFloat(matrixStrings[i + j * 4], ok);
448 if (!ok)
449 {
450 throw CiftiException("non-number in text of TransformationMatrixVoxelIndicesIJKtoXYZ: " + matrixStrings[i + j * 4]);
451 }
452 }
453 }
454 if (newsform[3][0] != 0.0f || newsform[3][1] != 0.0f || newsform[3][2] != 0.0f || newsform[3][3] != 1.0f)//yes, using equals, because those are all exact in float
455 {
456 throw CiftiException("last row of matrix in TransformationMatrixVoxelIndicesIJKtoXYZ must be 0 0 0 1");
457 }
458 if (XmlReader_readNextStartElement(xml))//find Volume end element
459 {
460 throw CiftiException("unexpected element in Volume: " + XmlReader_elementName(xml));
461 }
462 newsform *= mult;//apply units
463 newsform[3][3] = 1.0f;//reset [3][3], since it isn't spatial
464 setSpace(newDims, newsform.getMatrix());
465 CiftiAssert(XmlReader_checkEndElement(xml, "Volume"));
466 }
467
468 void VolumeSpace::writeCiftiXML1(XmlWriter& xml) const
469 {
470 xml.writeStartElement("Volume");
471 AString dimString = AString_number(m_dims[0]) + "," + AString_number(m_dims[1]) + "," + AString_number(m_dims[2]);
472 xml.writeAttribute("VolumeDimensions", dimString);
473 xml.writeStartElement("TransformationMatrixVoxelIndicesIJKtoXYZ");
474 xml.writeAttribute("DataSpace", "NIFTI_XFORM_UNKNOWN");//meaningless attribute
475 xml.writeAttribute("TransformedSpace", "NIFTI_XFORM_UNKNOWN");//removed in CIFTI-2, but apparently workbench has been writing this value for CIFTI-1
476 xml.writeAttribute("UnitsXYZ", "NIFTI_UNITS_MM");//only other choice in cifti-1 is micron, which we will probably never need in cifti-1
477 AString matrixString;
478 for (int j = 0; j < 3; ++j)
479 {
480 matrixString += "\n";
481 for (int i = 0; i < 4; ++i)
482 {
483 matrixString += AString_number_fixed(m_sform[j][i], 7) + " ";
484 }
485 }
486 matrixString += "\n";
487 for (int i = 0; i < 3; ++i)
488 {
489 matrixString += AString_number_fixed(0.0f, 7) + " ";
490 }
491 matrixString += AString_number_fixed(1.0f, 7);
492 xml.writeCharacters(matrixString);
493 xml.writeEndElement();//Transfor...
494 xml.writeEndElement();//Volume
495 }
496
497 void VolumeSpace::writeCiftiXML2(XmlWriter& xml) const
498 {
499 xml.writeStartElement("Volume");
500 AString dimString = AString_number(m_dims[0]) + "," + AString_number(m_dims[1]) + "," + AString_number(m_dims[2]);
501 xml.writeAttribute("VolumeDimensions", dimString);
502 xml.writeStartElement("TransformationMatrixVoxelIndicesIJKtoXYZ");
503 Vector3D vecs[4];
504 getSpacingVectors(vecs[0], vecs[1], vecs[2], vecs[3]);
505 float minLength = vecs[0].length();
506 for (int i = 1; i < 3; ++i)
507 {
508 minLength = min(minLength, vecs[i].length());
509 }
510 int myExponent = -3;//if we have a singular spatial dimension somehow, just use mm
511 if (minLength != 0.0f)
512 {
513 myExponent = 3 * (int)floor((log10(minLength) - log10(50.0f)) / 3.0f);//some magic to get the exponent that is a multiple of 3 that puts the length of the smallest spacing vector in [0.05, 50]
514 }
515 float multiplier = pow(10.0f, -3 - myExponent);//conversion factor from mm
516 xml.writeAttribute("MeterExponent", AString_number(myExponent));
517 AString matrixString;
518 for (int j = 0; j < 3; ++j)
519 {
520 matrixString += "\n";
521 for (int i = 0; i < 4; ++i)
522 {
523 matrixString += AString_number_fixed(m_sform[j][i] * multiplier, 7) + " ";
524 }
525 }
526 matrixString += "\n";
527 for (int i = 0; i < 3; ++i)
528 {
529 matrixString += AString_number_fixed(0.0f, 7) + " ";
530 }
531 matrixString += AString_number_fixed(1.0f, 7);//doesn't get multiplied, because it isn't spatial
532 xml.writeCharacters(matrixString);
533 xml.writeEndElement();//Transfor...
534 xml.writeEndElement();//Volume
535 }
0 #ifndef __VOLUME_SPACE_H__
1 #define __VOLUME_SPACE_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/Vector3D.h"
31
32 #include "Common/XmlAdapter.h"
33
34 #include "stdint.h"
35 #include <vector>
36
37 namespace cifti
38 {
39
40 class VolumeSpace
41 {
42 int64_t m_dims[3];
43 std::vector<std::vector<float> > m_sform, m_inverse;
44 void computeInverse();
45 public:
46 enum OrientTypes
47 {
48 LEFT_TO_RIGHT = 0,
49 RIGHT_TO_LEFT = 4,
50 POSTERIOR_TO_ANTERIOR = 1,
51 ANTERIOR_TO_POSTERIOR = 5,
52 INFERIOR_TO_SUPERIOR = 2,
53 SUPERIOR_TO_INFERIOR = 6
54 };
55 VolumeSpace();
56 VolumeSpace(const int64_t dims[3], const std::vector<std::vector<float> >& sform);
57 VolumeSpace(const int64_t dims[3], const float sform[12]);
58 void setSpace(const int64_t dims[3], const std::vector<std::vector<float> >& sform);
59 void setSpace(const int64_t dims[3], const float sform[12]);
60 const int64_t* getDims() const { return m_dims; }
61 const std::vector<std::vector<float> >& getSform() const { return m_sform; }
62 void getSpacingVectors(Vector3D& iStep, Vector3D& jStep, Vector3D& kStep, Vector3D& origin) const;
63 bool matchesVolumeSpace(const VolumeSpace& right) const;//allows slight mismatches
64 bool operator==(const VolumeSpace& right) const;//requires that it be exact
65 bool operator!=(const VolumeSpace& right) const { return !(*this == right); }
66
67 ///returns true if volume space is not skew, and each axis and index is separate
68 bool isPlumb() const;
69
70 ///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)
71 void getOrientAndSpacingForPlumb(OrientTypes* orientOut, float* spacingOut, float* originOut) const;
72
73 ///get just orientation, even for non-plumb volumes
74 void getOrientation(OrientTypes orientOut[3]) const;
75
76 ///returns coordinate triplet of an index triplet
77 template <typename T>
78 inline void indexToSpace(const T* indexIn, float* coordOut) const
79 { indexToSpace<T>(indexIn[0], indexIn[1], indexIn[2], coordOut[0], coordOut[1], coordOut[2]); }
80
81 ///returns coordinate triplet of three indices
82 template <typename T>
83 inline void indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3, float* coordOut) const
84 { indexToSpace<T>(indexIn1, indexIn2, indexIn3, coordOut[0], coordOut[1], coordOut[2]); }
85
86 ///returns three coordinates of an index triplet
87 template <typename T>
88 inline void indexToSpace(const T* indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const
89 { indexToSpace<T>(indexIn[0], indexIn[1], indexIn[2], coordOut1, coordOut2, coordOut3); }
90
91 ///returns three coordinates of three indices
92 template <typename T>
93 void indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const;
94
95 ///returns floating point index triplet of a given coordinate triplet
96 inline void spaceToIndex(const float* coordIn, float* indexOut) const { spaceToIndex(coordIn[0], coordIn[1], coordIn[2], indexOut[0], indexOut[1], indexOut[2]); }
97 ///returns floating point index triplet of three given coordinates
98 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]); }
99 ///returns three floating point indexes of a given coordinate triplet
100 inline void spaceToIndex(const float* coordIn, float& indexOut1, float& indexOut2, float& indexOut3) const { spaceToIndex(coordIn[0], coordIn[1], coordIn[2], indexOut1, indexOut2, indexOut3); }
101 ///returns three floating point indexes of three given coordinates
102 void spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float& indexOut1, float& indexOut2, float& indexOut3) const;
103
104 ///returns integer index triplet of voxel whose center is closest to the coordinate triplet
105 inline void enclosingVoxel(const float* coordIn, int64_t* indexOut) const { enclosingVoxel(coordIn[0], coordIn[1], coordIn[2], indexOut[0], indexOut[1], indexOut[2]); }
106 ///returns integer index triplet of voxel whose center is closest to the three coordinates
107 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]); }
108 ///returns integer indexes of voxel whose center is closest to the coordinate triplet
109 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); }
110 ///returns integer indexes of voxel whose center is closest to the three coordinates
111 void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const;
112
113 template <typename T>
114 inline bool indexValid(const T* indexIn) const
115 {
116 return indexValid(indexIn[0], indexIn[1], indexIn[2]);//implicit cast to int64_t
117 }
118
119 ///checks if an index is within array dimensions
120 inline bool indexValid(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3) const
121 {
122 if (indexIn1 < 0 || indexIn1 >= m_dims[0]) return false;
123 if (indexIn2 < 0 || indexIn2 >= m_dims[1]) return false;
124 if (indexIn3 < 0 || indexIn3 >= m_dims[2]) return false;
125 return true;
126 }
127
128 inline int64_t getIndex(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3) const
129 {
130 return indexIn1 + m_dims[0] * (indexIn2 + m_dims[1] * indexIn3);
131 }
132
133 template <typename T>
134 inline int64_t getIndex(const T* indexIn) const
135 {
136 return getIndex(indexIn[0], indexIn[1], indexIn[2]);//implicit cast to int64_t
137 }
138
139 void readCiftiXML1(XmlReader& xml);//xml functions
140 void readCiftiXML2(XmlReader& xml);
141 void writeCiftiXML1(XmlWriter& xml) const;
142 void writeCiftiXML2(XmlWriter& xml) const;
143 };
144
145 template <typename T>
146 void VolumeSpace::indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const
147 {
148 coordOut1 = indexIn1 * m_sform[0][0] + indexIn2 * m_sform[0][1] + indexIn3 * m_sform[0][2] + m_sform[0][3];
149 coordOut2 = indexIn1 * m_sform[1][0] + indexIn2 * m_sform[1][1] + indexIn3 * m_sform[1][2] + m_sform[1][3];
150 coordOut3 = indexIn1 * m_sform[2][0] + indexIn2 * m_sform[2][1] + indexIn3 * m_sform[2][2] + m_sform[2][3];
151 }
152
153 }
154
155 #endif //__VOLUME_SPACE_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiFile.h"
28
29 #include "Common/CiftiAssert.h"
30 #include "Common/MultiDimArray.h"
31 #include "NiftiIO.h"
32
33 #ifdef CIFTILIB_USE_QT
34 #include <QFileInfo>
35 #else
36 //use boost filesystem, because cross-platform filesystem support with POSIX is absurd
37 #define BOOST_FILESYSTEM_VERSION 3
38 #include "boost/filesystem.hpp"
39 #endif
40
41 #include <iostream>
42
43 using namespace std;
44 using namespace boost;
45 using namespace cifti;
46
47 //private implementation classes, helpers
48 namespace
49 {
50 class CiftiOnDiskImpl : public CiftiFile::WriteImplInterface
51 {
52 mutable NiftiIO m_nifti;//because file objects aren't stateless (current position), so reading "changes" them
53 CiftiXML m_xml;//because we need to parse it to set up the dimensions anyway
54 public:
55 CiftiOnDiskImpl(const AString& filename);//read-only
56 CiftiOnDiskImpl(const AString& filename, const CiftiXML& xml, const CiftiVersion& version, const bool& swapEndian);//make new empty file with read/write
57 void getRow(float* dataOut, const std::vector<int64_t>& indexSelect, const bool& tolerateShortRead) const;
58 void getColumn(float* dataOut, const int64_t& index) const;
59 const CiftiXML& getCiftiXML() const { return m_xml; }
60 AString getFilename() const { return m_nifti.getFilename(); }
61 bool isSwapped() const { return m_nifti.getHeader().isSwapped(); }
62 void setRow(const float* dataIn, const std::vector<int64_t>& indexSelect);
63 void setColumn(const float* dataIn, const int64_t& index);
64 };
65
66 class CiftiMemoryImpl : public CiftiFile::WriteImplInterface
67 {
68 MultiDimArray<float> m_array;
69 public:
70 CiftiMemoryImpl(const CiftiXML& xml);
71 void getRow(float* dataOut, const std::vector<int64_t>& indexSelect, const bool& tolerateShortRead) const;
72 void getColumn(float* dataOut, const int64_t& index) const;
73 bool isInMemory() const { return true; }
74 void setRow(const float* dataIn, const std::vector<int64_t>& indexSelect);
75 void setColumn(const float* dataIn, const int64_t& index);
76 };
77
78 bool shouldSwap(const CiftiFile::ENDIAN& endian)
79 {
80 if (ByteSwapping::isBigEndian())
81 {
82 if (endian == CiftiFile::LITTLE) return true;
83 } else {
84 if (endian == CiftiFile::BIG) return true;
85 }
86 return false;//default for all other enum values is to write native endian
87 }
88
89 bool dontRewrite(const CiftiFile::ENDIAN& endian)
90 {
91 return (endian == CiftiFile::ANY);
92 }
93
94 AString pathToAbsolute(const AString& mypath)
95 {
96 #ifdef CIFTILIB_USE_QT
97 return QFileInfo(mypath).absoluteFilePath();
98 #else
99 #ifdef CIFTILIB_BOOST_NO_FSV3
100 return filesystem::complete(AString_to_std_string(mypath)).file_string();
101 #else
102 return filesystem::absolute(AString_to_std_string(mypath)).native();
103 #endif
104 #endif
105 }
106
107 AString pathToCanonical(const AString& mypath)
108 {
109 #ifdef CIFTILIB_USE_QT
110 return QFileInfo(mypath).canonicalFilePath();
111 #else
112 #ifdef CIFTILIB_BOOST_NO_FSV3
113 return filesystem::complete(AString_to_std_string(mypath)).file_string();
114 #else
115 #ifdef CIFTILIB_BOOST_NO_CANONICAL
116 filesystem::path temp = AString_to_std_string(mypath);
117 if (!filesystem::exists(temp)) return "";
118 return absolute(temp).normalize().native();
119 #else
120 string temp = AString_to_std_string(mypath);
121 if (!filesystem::exists(temp)) return "";
122 return filesystem::canonical(temp).native();
123 #endif
124 #endif
125 #endif
126 }
127 }
128
129 CiftiFile::ReadImplInterface::~ReadImplInterface()
130 {
131 }
132
133 CiftiFile::WriteImplInterface::~WriteImplInterface()
134 {
135 }
136
137 CiftiFile::CiftiFile(const AString& fileName)
138 {
139 m_endianPref = NATIVE;
140 openFile(fileName);
141 }
142
143 void CiftiFile::openFile(const AString& fileName)
144 {
145 m_writingImpl.reset();
146 m_readingImpl.reset();//to make sure it closes everything first, even if the open throws
147 m_dims.clear();
148 boost::shared_ptr<CiftiOnDiskImpl> newRead(new CiftiOnDiskImpl(pathToAbsolute(fileName)));//this constructor opens existing file read-only
149 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
150 m_xml = newRead->getCiftiXML();
151 m_dims = m_xml.getDimensions();
152 m_onDiskVersion = m_xml.getParsedVersion();
153 }
154
155 void CiftiFile::setWritingFile(const AString& fileName, const CiftiVersion& writingVersion, const ENDIAN& endian)
156 {
157 m_writingFile = pathToAbsolute(fileName);//always resolve paths as soon as they enter CiftiFile, in case some clown changes directory before writing data
158 m_writingImpl.reset();//prevent writing to previous writing implementation, let the next set...() set up for writing
159 m_onDiskVersion = writingVersion;
160 m_endianPref = endian;
161 }
162
163 void CiftiFile::writeFile(const AString& fileName, const CiftiVersion& writingVersion, const ENDIAN& endian)
164 {
165 if (m_readingImpl == NULL || m_dims.empty()) throw CiftiException("writeFile called on uninitialized CiftiFile");
166 bool writeSwapped = shouldSwap(endian);
167 AString canonicalFilename = pathToCanonical(fileName);//NOTE: returns EMPTY STRING for nonexistant file
168 const CiftiOnDiskImpl* testImpl = dynamic_cast<CiftiOnDiskImpl*>(m_readingImpl.get());
169 bool collision = false, hadWriter = (m_writingImpl != NULL);
170 if (testImpl != NULL && canonicalFilename != "" && pathToCanonical(testImpl->getFilename()) == canonicalFilename)
171 {//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
172 if (m_onDiskVersion == writingVersion && (dontRewrite(endian) || writeSwapped == testImpl->isSwapped())) return;//don't need to copy to itself
173 collision = true;//we need to copy to memory temporarily
174 boost::shared_ptr<WriteImplInterface> tempMemory(new CiftiMemoryImpl(m_xml));//because tempRead is a ReadImpl, can't be used to copy to
175 copyImplData(m_readingImpl.get(), tempMemory.get(), m_dims);
176 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
177 m_writingImpl.reset();//and make it re-magic the writing implementation again if it tries to write again
178 }
179 boost::shared_ptr<WriteImplInterface> tempWrite(new CiftiOnDiskImpl(pathToAbsolute(fileName), m_xml, writingVersion, writeSwapped));
180 copyImplData(m_readingImpl.get(), tempWrite.get(), m_dims);
181 if (collision)//if we rewrote the file, we need the handle to the new file, and to dump the temporary in-memory version
182 {
183 m_onDiskVersion = writingVersion;//also record the current version number
184 m_readingImpl = tempWrite;//replace the temporary memory version
185 if (hadWriter)//if it was in read-write mode
186 {
187 m_writingImpl = tempWrite;//set the writer too
188 }
189 }
190 }
191
192 void CiftiFile::convertToInMemory()
193 {
194 if (isInMemory()) return;
195 if (m_readingImpl == NULL || m_dims.empty())//not set up yet
196 {
197 m_writingFile = "";//make sure it doesn't do on-disk when set...() is called
198 return;
199 }
200 boost::shared_ptr<WriteImplInterface> 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
201 copyImplData(m_readingImpl.get(), tempWrite.get(), m_dims);
202 m_writingImpl = tempWrite;
203 m_readingImpl = tempWrite;
204 }
205
206 bool CiftiFile::isInMemory() const
207 {
208 if (m_readingImpl == NULL)
209 {
210 return (m_writingFile == "");//return what it would be if verifyWriteImpl() was called
211 } else {
212 return m_readingImpl->isInMemory();
213 }
214 }
215
216 void CiftiFile::getRow(float* dataOut, const vector<int64_t>& indexSelect, const bool& tolerateShortRead) const
217 {
218 if (m_dims.empty()) throw CiftiException("getRow called on uninitialized CiftiFile");
219 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
220 m_readingImpl->getRow(dataOut, indexSelect, tolerateShortRead);
221 }
222
223 void CiftiFile::getColumn(float* dataOut, const int64_t& index) const
224 {
225 if (m_dims.empty()) throw CiftiException("getColumn called on uninitialized CiftiFile");
226 if (m_dims.size() != 2) throw CiftiException("getColumn called on non-2D CiftiFile");
227 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
228 m_readingImpl->getColumn(dataOut, index);
229 }
230
231 void CiftiFile::setCiftiXML(const CiftiXML& xml, const bool useOldMetadata)
232 {
233 m_readingImpl.reset();//drop old implementation, as it is now invalid due to XML (and therefore matrix size) change
234 m_writingImpl.reset();
235 if (xml.getNumberOfDimensions() == 0) throw CiftiException("setCiftiXML called with 0-dimensional CiftiXML");
236 if (useOldMetadata)
237 {
238 MetaData newmd = m_xml.getFileMetaData();//make a copy
239 m_xml = xml;//because this will overwrite the metadata
240 m_xml.setFileMetaData(newmd);
241 } else {
242 m_xml = xml;
243 }
244 m_dims = m_xml.getDimensions();
245 for (size_t i = 0; i < m_dims.size(); ++i)
246 {
247 if (m_dims[i] < 1) throw CiftiException("cifti xml dimensions must be greater than zero");
248 }
249 }
250
251 void CiftiFile::setRow(const float* dataIn, const vector<int64_t>& indexSelect)
252 {
253 verifyWriteImpl();
254 m_writingImpl->setRow(dataIn, indexSelect);
255 }
256
257 void CiftiFile::setColumn(const float* dataIn, const int64_t& index)
258 {
259 verifyWriteImpl();
260 if (m_dims.size() != 2) throw CiftiException("setColumn called on non-2D CiftiFile");
261 m_writingImpl->setColumn(dataIn, index);
262 }
263
264 //single-index functions
265 void CiftiFile::getRow(float* dataOut, const int64_t& index, const bool& tolerateShortRead) const
266 {
267 if (m_dims.empty()) throw CiftiException("getRow called on uninitialized CiftiFile");
268 if (m_dims.size() != 2) throw CiftiException("getRow with single index called on non-2D CiftiFile");
269 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
270 vector<int64_t> tempvec(1, index);//could use a member if we need more speed
271 m_readingImpl->getRow(dataOut, tempvec, tolerateShortRead);
272 }
273
274 void CiftiFile::setRow(const float* dataIn, const int64_t& index)
275 {
276 verifyWriteImpl();
277 if (m_dims.size() != 2) throw CiftiException("setRow with single index called on non-2D CiftiFile");
278 vector<int64_t> tempvec(1, index);//could use a member if we need more speed
279 m_writingImpl->setRow(dataIn, tempvec);
280 }
281 //*///end single-index functions
282
283 void CiftiFile::verifyWriteImpl()
284 {//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
285 if (m_writingImpl != NULL) return;
286 CiftiAssert(!m_dims.empty());//if the xml hasn't been set, then we can't do anything meaningful
287 if (m_dims.empty()) throw CiftiException("setRow or setColumn attempted on uninitialized CiftiFile");
288 if (m_writingFile == "")
289 {
290 if (m_readingImpl != NULL)
291 {
292 convertToInMemory();
293 } else {
294 m_writingImpl = boost::shared_ptr<CiftiMemoryImpl>(new CiftiMemoryImpl(m_xml));
295 }
296 } else {//NOTE: m_onDiskVersion gets set in setWritingFile
297 if (m_readingImpl != NULL)
298 {
299 CiftiOnDiskImpl* testImpl = dynamic_cast<CiftiOnDiskImpl*>(m_readingImpl.get());
300 if (testImpl != NULL)
301 {
302 AString canonicalCurrent = pathToCanonical(testImpl->getFilename());//returns "" if nonexistant, if unlinked while open
303 if (canonicalCurrent != "" && canonicalCurrent == pathToCanonical(m_writingFile))//these were already absolute
304 {
305 convertToInMemory();//save existing data in memory before we clobber file
306 }
307 }
308 }
309 m_writingImpl = boost::shared_ptr<CiftiOnDiskImpl>(new CiftiOnDiskImpl(m_writingFile, m_xml, m_onDiskVersion, shouldSwap(m_endianPref)));//this constructor makes new file for writing
310 if (m_readingImpl != NULL)
311 {
312 copyImplData(m_readingImpl.get(), m_writingImpl.get(), m_dims);
313 }
314 }
315 m_readingImpl = m_writingImpl;//read-only implementations are set up in specialized functions
316 }
317
318 void CiftiFile::copyImplData(const ReadImplInterface* from, WriteImplInterface* to, const vector<int64_t>& dims)
319 {
320 vector<int64_t> iterateDims(dims.begin() + 1, dims.end());
321 vector<float> scratchRow(dims[0]);
322 for (MultiDimIterator<int64_t> iter(iterateDims); !iter.atEnd(); ++iter)
323 {
324 from->getRow(scratchRow.data(), *iter, false);
325 to->setRow(scratchRow.data(), *iter);
326 }
327 }
328
329 CiftiMemoryImpl::CiftiMemoryImpl(const CiftiXML& xml)
330 {
331 CiftiAssert(xml.getNumberOfDimensions() != 0);
332 m_array.resize(xml.getDimensions());
333 }
334
335 void CiftiMemoryImpl::getRow(float* dataOut, const vector<int64_t>& indexSelect, const bool&) const
336 {
337 const float* ref = m_array.get(1, indexSelect);
338 int64_t rowSize = m_array.getDimensions()[0];//we don't accept 0-D CiftiXML, so this will always work
339 for (int64_t i = 0; i < rowSize; ++i)
340 {
341 dataOut[i] = ref[i];
342 }
343 }
344
345 void CiftiMemoryImpl::getColumn(float* dataOut, const int64_t& index) const
346 {
347 CiftiAssert(m_array.getDimensions().size() == 2);//otherwise, CiftiFile shouldn't have called this
348 const float* ref = m_array.get(2, vector<int64_t>());//empty vector is intentional, only 2 dimensions exist, so no more to select from
349 int64_t rowSize = m_array.getDimensions()[0];
350 int64_t colSize = m_array.getDimensions()[1];
351 CiftiAssert(index >= 0 && index < rowSize);//because we are doing the indexing math manually for speed
352 for (int64_t i = 0; i < colSize; ++i)
353 {
354 dataOut[i] = ref[index + rowSize * i];
355 }
356 }
357
358 void CiftiMemoryImpl::setRow(const float* dataIn, const vector<int64_t>& indexSelect)
359 {
360 float* ref = m_array.get(1, indexSelect);
361 int64_t rowSize = m_array.getDimensions()[0];//we don't accept 0-D CiftiXML, so this will always work
362 for (int64_t i = 0; i < rowSize; ++i)
363 {
364 ref[i] = dataIn[i];
365 }
366 }
367
368 void CiftiMemoryImpl::setColumn(const float* dataIn, const int64_t& index)
369 {
370 CiftiAssert(m_array.getDimensions().size() == 2);//otherwise, CiftiFile shouldn't have called this
371 float* ref = m_array.get(2, vector<int64_t>());//empty vector is intentional, only 2 dimensions exist, so no more to select from
372 int64_t rowSize = m_array.getDimensions()[0];
373 int64_t colSize = m_array.getDimensions()[1];
374 CiftiAssert(index >= 0 && index < rowSize);//because we are doing the indexing math manually for speed
375 for (int64_t i = 0; i < colSize; ++i)
376 {
377 ref[index + rowSize * i] = dataIn[i];
378 }
379 }
380
381 CiftiOnDiskImpl::CiftiOnDiskImpl(const AString& filename)
382 {//opens existing file for reading
383 m_nifti.openRead(filename);//read-only, so we don't need write permission to read a cifti file
384 if (m_nifti.getNumComponents() != 1) throw CiftiException("complex or rgb datatype found in file '" + filename + "', these are not supported in cifti");
385 const NiftiHeader& myHeader = m_nifti.getHeader();
386 int numExts = (int)myHeader.m_extensions.size(), whichExt = -1;
387 for (int i = 0; i < numExts; ++i)
388 {
389 if (myHeader.m_extensions[i]->m_ecode == NIFTI_ECODE_CIFTI)
390 {
391 whichExt = i;
392 break;
393 }
394 }
395 if (whichExt == -1) throw CiftiException("no cifti extension found in file '" + filename + "'");
396 m_xml.readXML(myHeader.m_extensions[whichExt]->m_bytes);
397 vector<int64_t> dimCheck = m_nifti.getDimensions();
398 if (dimCheck.size() < 5) throw CiftiException("invalid dimensions in cifti file '" + filename + "'");
399 for (int i = 0; i < 4; ++i)
400 {
401 if (dimCheck[i] != 1) throw CiftiException("non-singular dimension #" + AString_number(i + 1) + " in cifti file '" + filename + "'");
402 }
403 if (m_xml.getParsedVersion().hasReversedFirstDims())
404 {
405 while (dimCheck.size() < 6) dimCheck.push_back(1);//just in case
406 int64_t temp = dimCheck[4];//note: nifti dim[5] is the 5th dimension, index 4 in this vector
407 dimCheck[4] = dimCheck[5];
408 dimCheck[5] = temp;
409 m_nifti.overrideDimensions(dimCheck);
410 }
411 if (m_xml.getNumberOfDimensions() + 4 != (int)dimCheck.size()) throw CiftiException("XML does not match number of nifti dimensions in file " + filename + "'");
412 for (int i = 4; i < (int)dimCheck.size(); ++i)
413 {
414 if (m_xml.getDimensionLength(i - 4) < 1)//CiftiXML will only let this happen with cifti-1
415 {
416 m_xml.getSeriesMap(i - 4).setLength(dimCheck[i]);//and only in a series map
417 } else {
418 if (m_xml.getDimensionLength(i - 4) != dimCheck[i])
419 {
420 throw CiftiException("xml and nifti header disagree on matrix dimensions");
421 }
422 }
423 }
424 }
425
426 CiftiOnDiskImpl::CiftiOnDiskImpl(const AString& filename, const CiftiXML& xml, const CiftiVersion& version, const bool& swapEndian)
427 {//starts writing new file
428 NiftiHeader outHeader;
429 outHeader.setDataType(NIFTI_TYPE_FLOAT32);//actually redundant currently, default is float32
430 char intentName[16];
431 int32_t intentCode = xml.getIntentInfo(version, intentName);
432 outHeader.setIntent(intentCode, intentName);
433 boost::shared_ptr<NiftiExtension> outExtension(new NiftiExtension());
434 outExtension->m_ecode = NIFTI_ECODE_CIFTI;
435 outExtension->m_bytes = xml.writeXMLToVector(version);
436 outHeader.m_extensions.push_back(outExtension);
437 vector<int64_t> matrixDims = xml.getDimensions();
438 vector<int64_t> niftiDims(4, 1);//the reserved space and time dims
439 niftiDims.insert(niftiDims.end(), matrixDims.begin(), matrixDims.end());
440 if (version.hasReversedFirstDims())
441 {
442 vector<int64_t> headerDims = niftiDims;
443 while (headerDims.size() < 6) headerDims.push_back(1);//just in case
444 int64_t temp = headerDims[4];
445 headerDims[4] = headerDims[5];
446 headerDims[5] = temp;
447 outHeader.setDimensions(headerDims);//give the header the reversed dimensions
448 m_nifti.writeNew(filename, outHeader, 2, true, swapEndian);
449 m_nifti.overrideDimensions(niftiDims);//and then tell the nifti reader to use the correct dimensions
450 } else {
451 outHeader.setDimensions(niftiDims);
452 m_nifti.writeNew(filename, outHeader, 2, true, swapEndian);
453 }
454 m_xml = xml;
455 }
456
457 void CiftiOnDiskImpl::getRow(float* dataOut, const vector<int64_t>& indexSelect, const bool& tolerateShortRead) const
458 {
459 m_nifti.readData(dataOut, 5, indexSelect, tolerateShortRead);//5 means 4 reserved (space and time) plus the first cifti dimension
460 }
461
462 void CiftiOnDiskImpl::getColumn(float* dataOut, const int64_t& index) const
463 {
464 CiftiAssert(m_xml.getNumberOfDimensions() == 2);//otherwise this shouldn't be called
465 CiftiAssert(index >= 0 && index < m_xml.getDimensionLength(CiftiXML::ALONG_ROW));
466 vector<int64_t> indexSelect(2);
467 indexSelect[0] = index;
468 int64_t colLength = m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN);
469 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
470 {
471 indexSelect[1] = i;
472 m_nifti.readData(dataOut + i, 4, indexSelect);//4 means just the 4 reserved dimensions, so 1 element of the matrix
473 }
474 }
475
476 void CiftiOnDiskImpl::setRow(const float* dataIn, const vector<int64_t>& indexSelect)
477 {
478 m_nifti.writeData(dataIn, 5, indexSelect);
479 }
480
481 void CiftiOnDiskImpl::setColumn(const float* dataIn, const int64_t& index)
482 {
483 CiftiAssert(m_xml.getNumberOfDimensions() == 2);//otherwise this shouldn't be called
484 CiftiAssert(index >= 0 && index < m_xml.getDimensionLength(CiftiXML::ALONG_ROW));
485 vector<int64_t> indexSelect(2);
486 indexSelect[0] = index;
487 int64_t colLength = m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN);
488 for (int64_t i = 0; i < colLength; ++i)//don't do RMW, so write it 1 element at a time
489 {
490 indexSelect[1] = i;
491 m_nifti.writeData(dataIn + i, 4, indexSelect);//4 means just the 4 reserved dimensions, so 1 element of the matrix
492 }
493 }
0 #ifndef __CIFTI_FILE_H__
1 #define __CIFTI_FILE_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/AString.h"
31 #include "Common/CiftiException.h"
32 #include "Common/MultiDimIterator.h"
33 #include "Cifti/CiftiXML.h"
34
35 #include "boost/shared_ptr.hpp"
36
37 #include <vector>
38
39 ///namespace for all CiftiLib functionality
40 namespace cifti
41 {
42 ///class for reading and writing cifti files
43 class CiftiFile
44 {
45 public:
46
47 enum ENDIAN
48 {
49 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
50 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
51 LITTLE,
52 BIG
53 };
54
55 CiftiFile() { m_endianPref = NATIVE; }
56
57 ///starts on-disk reading
58 explicit CiftiFile(const AString &fileName);
59
60 ///starts on-disk reading
61 void openFile(const AString& fileName);
62
63 ///starts on-disk writing
64 void setWritingFile(const AString& fileName, const CiftiVersion& writingVersion = CiftiVersion(), const ENDIAN& endian = NATIVE);
65
66 ///does nothing if filename, version, and effective endianness match file currently open, otherwise writes complete file
67 void writeFile(const AString& fileName, const CiftiVersion& writingVersion = CiftiVersion(), const ENDIAN& endian = ANY);
68
69 ///reads file into memory, closes file
70 void convertToInMemory();
71
72 const CiftiXML& getCiftiXML() const { return m_xml; }
73 bool isInMemory() const;
74
75 ///the tolerateShortRead parameter is useful for on-disk writing when it is easiest to do RMW multiple times on a new file
76 void getRow(float* dataOut, const std::vector<int64_t>& indexSelect, const bool& tolerateShortRead = false) const;
77 const std::vector<int64_t>& getDimensions() const { return m_dims; }
78
79 ///convenience function for iterating over arbitrary numbers of dimensions
80 MultiDimIterator<int64_t> getIteratorOverRows() const
81 {
82 return MultiDimIterator<int64_t>(std::vector<int64_t>(m_dims.begin() + 1, m_dims.end()));
83 }
84
85 ///for 2D only, will be slow if on disk!
86 void getColumn(float* dataOut, const int64_t& index) const;
87
88 void setCiftiXML(const CiftiXML& xml, const bool useOldMetadata = true);
89 void setRow(const float* dataIn, const std::vector<int64_t>& indexSelect);
90
91 ///for 2D only, will be slow if on disk!
92 void setColumn(const float* dataIn, const int64_t& index);
93
94 ///for 2D only, if you don't want to pass a vector for indexing
95 void getRow(float* dataOut, const int64_t& index, const bool& tolerateShortRead = false) const;
96
97 ///for 2D only, if you don't want to pass a vector for indexing
98 void setRow(const float* dataIn, const int64_t& index);
99
100 //implementation details from here down
101 class ReadImplInterface
102 {
103 public:
104 virtual void getRow(float* dataOut, const std::vector<int64_t>& indexSelect, const bool& tolerateShortRead) const = 0;
105 virtual void getColumn(float* dataOut, const int64_t& index) const = 0;
106 virtual bool isInMemory() const { return false; }
107 virtual ~ReadImplInterface();
108 };
109 //assume if you can write to it, you can also read from it
110 class WriteImplInterface : public ReadImplInterface
111 {
112 public:
113 virtual void setRow(const float* dataIn, const std::vector<int64_t>& indexSelect) = 0;
114 virtual void setColumn(const float* dataIn, const int64_t& index) = 0;
115 virtual ~WriteImplInterface();
116 };
117 private:
118 std::vector<int64_t> m_dims;
119 boost::shared_ptr<WriteImplInterface> m_writingImpl;//this will be equal to m_readingImpl when non-null
120 boost::shared_ptr<ReadImplInterface> m_readingImpl;
121 AString m_writingFile;
122 CiftiXML m_xml;
123 CiftiVersion m_onDiskVersion;
124 ENDIAN m_endianPref;
125
126 void verifyWriteImpl();
127 static void copyImplData(const ReadImplInterface* from, WriteImplInterface* to, const std::vector<int64_t>& dims);
128 };
129
130 }
131
132 #endif //__CIFTI_FILE_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "AString.h"
28
29 #ifdef CIFTILIB_USE_QT
30 #include <QStringList>
31 #endif
32
33 #ifdef CIFTILIB_USE_XMLPP
34 #include "boost/lexical_cast.hpp"
35 using namespace boost;
36 #endif
37
38 using namespace std;
39 using namespace cifti;
40
41 vector<AString> cifti::AString_split(const AString& input, const char& delim)
42 {
43 vector<AString> ret;
44 #ifdef CIFTILIB_USE_QT
45 QStringList temp = input.split(delim);
46 int listSize = temp.size();//yes, QT uses int...
47 ret.resize(listSize);
48 for (int i = 0; i < listSize; ++i)
49 {
50 ret[i] = temp[i];
51 }
52 #else
53 #ifdef CIFTILIB_USE_XMLPP
54 size_t start = 0, end = input.find(delim);
55 while (end != AString::npos)
56 {
57 ret.push_back(input.substr(start, end - start));
58 start = end + 1;
59 end = input.find(delim, start);
60 }
61 ret.push_back(input.substr(start));
62 #else
63 #error "not implemented"
64 #endif
65 #endif
66 return ret;
67 }
68
69 vector<AString> cifti::AString_split_whitespace(const AString& input)
70 {
71 vector<AString> ret;
72 #ifdef CIFTILIB_USE_QT
73 QStringList temp = input.split(QRegExp("\\s+"), QString::SkipEmptyParts);
74 int listSize = temp.size();//yes, QT uses int...
75 ret.resize(listSize);
76 for (int i = 0; i < listSize; ++i)
77 {
78 ret[i] = temp[i];
79 }
80 #else
81 #ifdef CIFTILIB_USE_XMLPP
82 AString::const_iterator iter = input.begin(), end = input.end();
83 while (iter != end)
84 {
85 while (iter != end && g_unichar_isspace(*iter)) ++iter;//skip spaces, including at start of input
86 if (iter == end) break;//ignore spaces on end of input
87 AString::const_iterator start = iter;
88 while (iter != end && !g_unichar_isspace(*iter)) ++iter;//continue to space or end
89 ret.push_back(AString(start, iter));
90 }
91 #else
92 #error "not implemented"
93 #endif
94 #endif
95 return ret;
96 }
97
98 int64_t cifti::AString_toInt(const AString& input, bool& ok)
99 {
100 #ifdef CIFTILIB_USE_QT
101 return input.toLongLong(&ok);
102 #else
103 #ifdef CIFTILIB_USE_XMLPP
104 #ifdef CIFTILIB_BOOST_NO_TRY_LEXICAL
105 try
106 {
107 ok = true;
108 return lexical_cast<int64_t>(input);
109 } catch (...) {
110 ok = false;
111 return 0;
112 }
113 #else
114 int64_t ret;
115 ok = conversion::try_lexical_convert(input, ret);
116 if (!ok) ret = 0;
117 return ret;
118 #endif
119 #else
120 #error "not implemented"
121 #endif
122 #endif
123 }
124
125 float cifti::AString_toFloat(const AString& input, bool& ok)
126 {
127 #ifdef CIFTILIB_USE_QT
128 return input.toFloat(&ok);
129 #else
130 #ifdef CIFTILIB_USE_XMLPP
131 #ifdef CIFTILIB_BOOST_NO_TRY_LEXICAL
132 try
133 {
134 ok = true;
135 return lexical_cast<float>(input);
136 } catch (...) {
137 ok = false;
138 return 0.0f;
139 }
140 #else
141 float ret;
142 ok = conversion::try_lexical_convert(input, ret);
143 if (!ok) ret = 0.0f;
144 return ret;
145 #endif
146 #else
147 #error "not implemented"
148 #endif
149 #endif
150 }
0 #ifndef __ASTRING_H__
1 #define __ASTRING_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <string>
31 #include <vector>
32
33 #include "stdint.h"
34
35 #ifdef __ASTRING_H_HAVE_IMPL__
36 #undef __ASTRING_H_HAVE_IMPL__
37 #endif
38
39 #ifdef CIFTILIB_USE_QT
40 #define __ASTRING_H_HAVE_IMPL__
41 #include <QString>
42 namespace cifti
43 {
44 typedef QString AString;
45 #define ASTRING_TO_CSTR(mystr) ((mystr).toLocal8Bit().constData())
46 #define ASTRING_UTF8_RAW(mystr) ((mystr).toUtf8().constData())
47 inline std::string AString_to_std_string(const AString& mystr)
48 {
49 QByteArray temparray = mystr.toLocal8Bit();
50 return std::string(temparray.constData(), temparray.size());
51 }
52 inline AString AString_from_latin1(const char* data, const int& size)
53 {
54 return QString::fromLatin1(data, size);
55 }
56 inline AString AString_substr(const AString& mystr, const int& first, const int& count = -1)
57 {
58 return mystr.mid(first, count);
59 }
60 template <typename T>
61 AString AString_number(const T& num)
62 {
63 return QString::number(num);
64 }
65 template <typename T>
66 AString AString_number_fixed(const T& num, const int& numDecimals)
67 {
68 return QString::number(num, 'f', numDecimals);
69 }
70 }
71 #endif //CIFTILIB_USE_QT
72
73 #ifdef CIFTILIB_USE_XMLPP
74 #define __ASTRING_H_HAVE_IMPL__
75 #include "glibmm/convert.h"
76 #include "glibmm/ustring.h"
77 #include <iomanip>
78 namespace cifti
79 {
80 typedef Glib::ustring AString;
81 #define ASTRING_TO_CSTR(mystr) (Glib::locale_from_utf8((mystr)).c_str())
82 #define ASTRING_UTF8_RAW(mystr) ((mystr).data())
83 inline std::string AString_to_std_string(const AString& mystr)
84 {
85 return Glib::locale_from_utf8(mystr);
86 }
87 inline AString AString_from_latin1(const char* data, const int& size)
88 {
89 return Glib::convert(std::string(data, size), "UTF-8", "ISO-8859-1");
90 }
91 inline AString AString_substr(const AString& mystr, const Glib::ustring::size_type& first, const Glib::ustring::size_type& count = std::string::npos)
92 {//HACK: Glib::ustring::npos is undefined at link time with glibmm 2.4 for unknown reasons, but the header says it is equal to std::string's, so use it instead
93 return mystr.substr(first, count);
94 }
95 template <typename T>
96 AString AString_number(const T& num)
97 {
98 return Glib::ustring::format(num);
99 }
100 template <typename T>
101 AString AString_number_fixed(const T& num, const int& numDecimals)
102 {
103 return Glib::ustring::format(std::fixed, std::setprecision(numDecimals), num);
104 }
105 }
106 #endif //CIFTILIB_USE_XMLPP
107
108 #ifndef __ASTRING_H_HAVE_IMPL__
109 #error "you must define either CIFTILIB_USE_QT or CIFTILIB_USE_XMLPP to select what unicode string implementation to use"
110 #endif
111
112 namespace cifti
113 {
114 //more helper functions
115 std::vector<AString> AString_split(const AString& input, const char& delim);
116 std::vector<AString> AString_split_whitespace(const AString& input);
117 int64_t AString_toInt(const AString& input, bool& ok);
118 float AString_toFloat(const AString& input, bool& ok);
119 }
120
121 #endif //__ASTRING_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 //try to force large file support from zlib, any other file reading calls
28 #ifndef CIFTILIB_OS_MACOSX
29 #define _LARGEFILE64_SOURCE
30 #define _LFS64_LARGEFILE 1
31 #define _FILE_OFFSET_BITS 64
32 #endif
33
34 #include "BinaryFile.h"
35 #include "CiftiAssert.h"
36 #include "CiftiException.h"
37
38 #ifdef CIFTILIB_USE_QT
39 #include <QFile>
40 #else
41 #include "stdio.h"
42 #include "errno.h"
43 #define BOOST_FILESYSTEM_VERSION 3
44 #include "boost/filesystem.hpp"
45 #endif
46
47 #ifdef CIFTILIB_HAVE_ZLIB
48 #include "zlib.h"
49 #endif //CIFTILIB_HAVE_ZLIB
50
51 #include <algorithm>
52 #include <iostream>
53
54 using namespace cifti;
55 using boost::shared_ptr;
56 using namespace std;
57
58 //private implementation classes
59 namespace cifti
60 {
61 #ifdef ZLIB_VERSION
62 class ZFileImpl : public BinaryFile::ImplInterface
63 {
64 gzFile m_zfile;
65 const static int64_t CHUNK_SIZE;
66 public:
67 ZFileImpl() { m_zfile = NULL; }
68 void open(const AString& filename, const BinaryFile::OpenMode& opmode);
69 void close();
70 void seek(const int64_t& position);
71 int64_t pos();
72 void read(void* dataOut, const int64_t& count, int64_t* numRead);
73 void write(const void* dataIn, const int64_t& count);
74 ~ZFileImpl();
75 };
76
77 const int64_t ZFileImpl::CHUNK_SIZE = 1<<26;//64MiB, large enough for good performance, small enough for zlib, must convert to uint32
78 #endif //ZLIB_VERSION
79
80 #ifdef CIFTILIB_USE_QT
81 class QFileImpl : public BinaryFile::ImplInterface
82 {
83 QFile m_file;
84 const static int64_t CHUNK_SIZE;
85 public:
86 void open(const AString& filename, const BinaryFile::OpenMode& opmode);
87 void close();
88 void seek(const int64_t& position);
89 int64_t pos();
90 void read(void* dataOut, const int64_t& count, int64_t* numRead);
91 void write(const void* dataIn, const int64_t& count);
92 };
93
94 const int64_t QFileImpl::CHUNK_SIZE = 1<<30;//1GiB, QT4 apparently chokes at more than 2GiB via buffer.read using int32
95 #else
96 class StrFileImpl : public BinaryFile::ImplInterface
97 {
98 FILE* m_file;
99 int64_t m_curPos;//so we can avoid calling seek when it is to current position - QFile does this, and it makes it much faster for some cases
100 public:
101 StrFileImpl() { m_file = NULL; m_curPos = -1; }
102 void open(const AString& filename, const BinaryFile::OpenMode& opmode);
103 void close();
104 void seek(const int64_t& position);
105 int64_t pos();
106 void read(void* dataOut, const int64_t& count, int64_t* numRead);
107 void write(const void* dataIn, const int64_t& count);
108 ~StrFileImpl();
109 };
110 #endif //CIFTILIB_USE_QT
111 }
112
113 BinaryFile::ImplInterface::~ImplInterface()
114 {
115 }
116
117 BinaryFile::BinaryFile(const AString& filename, const OpenMode& fileMode)
118 {
119 open(filename, fileMode);
120 }
121
122 void BinaryFile::close()
123 {
124 m_curMode = NONE;
125 if (m_impl == NULL) return;
126 m_impl->close();
127 m_impl.reset();
128 }
129
130 AString BinaryFile::getFilename() const
131 {
132 if (m_impl == NULL) return "";//don't throw, its not really a problem
133 return m_impl->getFilename();
134 }
135
136 bool BinaryFile::getOpenForRead()
137 {
138 return (m_curMode | READ) != 0;
139 }
140
141 bool BinaryFile::getOpenForWrite()
142 {
143 return (m_curMode | WRITE) != 0;
144 }
145
146 void BinaryFile::open(const AString& filename, const OpenMode& opmode)
147 {
148 close();
149 if (opmode == NONE) throw CiftiException("can't open file with NONE mode");
150 if (AString_substr(filename, filename.size() - 3) == ".gz")
151 {
152 #ifdef ZLIB_VERSION
153 m_impl = boost::shared_ptr<ZFileImpl>(new ZFileImpl());
154 #else //ZLIB_VERSION
155 throw CiftiException("can't open .gz file '" + filename + "', compiled without zlib support");
156 #endif //ZLIB_VERSION
157 } else {
158 #ifdef CIFTILIB_USE_QT
159 m_impl = boost::shared_ptr<QFileImpl>(new QFileImpl());
160 #else
161 m_impl = boost::shared_ptr<StrFileImpl>(new StrFileImpl());
162 #endif
163 }
164 m_impl->open(filename, opmode);
165 m_curMode = opmode;
166 }
167
168 void BinaryFile::read(void* dataOut, const int64_t& count, int64_t* numRead)
169 {
170 CiftiAssert(count >= 0);//not sure about allowing 0
171 if (!getOpenForRead()) throw CiftiException("file is not open for reading");
172 m_impl->read(dataOut, count, numRead);
173 }
174
175 void BinaryFile::seek(const int64_t& position)
176 {
177 CiftiAssert(position >= 0);
178 if (m_curMode == NONE) throw CiftiException("file is not open, can't seek");
179 m_impl->seek(position);
180 }
181
182 int64_t BinaryFile::pos()
183 {
184 if (m_curMode == NONE) throw CiftiException("file is not open, can't report position");
185 return m_impl->pos();
186 }
187
188 void BinaryFile::write(const void* dataIn, const int64_t& count)
189 {
190 CiftiAssert(count >= 0);//not sure about allowing 0
191 if (!getOpenForWrite()) throw CiftiException("file is not open for writing");
192 m_impl->write(dataIn, count);
193 }
194
195 #ifdef ZLIB_VERSION
196 void ZFileImpl::open(const AString& filename, const BinaryFile::OpenMode& opmode)
197 {
198 close();//don't need to, but just because
199 m_fileName = filename;
200 const char* mode = NULL;
201 switch (opmode)//we only support a limited number of combinations, and the string modes are quirky
202 {
203 case BinaryFile::READ:
204 mode = "rb";
205 break;
206 case BinaryFile::WRITE_TRUNCATE:
207 mode = "wb";//you have to do "r+b" in order to ask it to not truncate, which zlib doesn't support anyway
208 break;
209 default:
210 throw CiftiException("compressed file only supports READ and WRITE_TRUNCATE modes");
211 }
212 #if !defined(CIFTILIB_OS_MACOSX) && ZLIB_VERNUM > 0x1232
213 m_zfile = gzopen64(ASTRING_TO_CSTR(filename), mode);
214 #else
215 m_zfile = gzopen(ASTRING_TO_CSTR(filename), mode);
216 #endif
217 if (m_zfile == NULL)
218 {
219 #ifdef CIFTILIB_USE_QT
220 if (QFile::exists(filename))
221 #else
222 if (boost::filesystem::exists(AString_to_std_string(filename)))
223 #endif
224 {
225 if (!(opmode & BinaryFile::TRUNCATE))
226 {
227 throw CiftiException("failed to open compressed file '" + filename + "', file does not exist, or folder permissions prevent seeing it");
228 } else {//m_file.error() doesn't help identify this case, see below
229 throw CiftiException("failed to open compressed file '" + filename + "', unable to create file");
230 }
231 }
232 throw CiftiException("failed to open compressed file '" + filename + "'");
233 }
234 }
235
236 void ZFileImpl::close()
237 {
238 if (m_zfile == NULL) return;//happens when closed and then destroyed, error opening
239 gzflush(m_zfile, Z_FULL_FLUSH);
240 if (gzclose(m_zfile) != 0) throw CiftiException("error closing compressed file '" + m_fileName + "'");
241 m_zfile = NULL;
242 }
243
244 void ZFileImpl::read(void* dataOut, const int64_t& count, int64_t* numRead)
245 {
246 if (m_zfile == NULL) throw CiftiException("read called on unopened ZFileImpl");//shouldn't happen
247 int64_t totalRead = 0;
248 int readret = 0;//to preserve the info of the read that broke early
249 while (totalRead < count)
250 {
251 int64_t iterSize = min(count - totalRead, CHUNK_SIZE);
252 readret = gzread(m_zfile, ((char*)dataOut) + totalRead, iterSize);
253 if (readret < 1) break;//0 or -1 indicate eof or error
254 totalRead += readret;
255 }
256 if (numRead == NULL)
257 {
258 if (totalRead != count)
259 {
260 if (readret < 0) throw CiftiException("error while reading compressed file '" + m_fileName + "'");
261 throw CiftiException("premature end of file in compressed file '" + m_fileName + "'");
262 }
263 } else {
264 *numRead = totalRead;
265 }
266 }
267
268 void ZFileImpl::seek(const int64_t& position)
269 {
270 if (m_zfile == NULL) throw CiftiException("seek called on unopened ZFileImpl");//shouldn't happen
271 if (pos() == position) return;//slight hack, since gzseek is slow or nonfunctional for some cases, so don't try it unless necessary
272 #if !defined(CIFTILIB_OS_MACOSX) && ZLIB_VERNUM > 0x1232
273 int64_t ret = gzseek64(m_zfile, position, SEEK_SET);
274 #else
275 int64_t ret = gzseek(m_zfile, position, SEEK_SET);
276 #endif
277 if (ret != position) throw CiftiException("seek failed in compressed file '" + m_fileName + "'");
278 }
279
280 int64_t ZFileImpl::pos()
281 {
282 if (m_zfile == NULL) throw CiftiException("pos called on unopened ZFileImpl");//shouldn't happen
283 #if !defined(CIFTILIB_OS_MACOSX) && ZLIB_VERNUM > 0x1232
284 return gztell64(m_zfile);
285 #else
286 return gztell(m_zfile);
287 #endif
288 }
289
290 void ZFileImpl::write(const void* dataIn, const int64_t& count)
291 {
292 if (m_zfile == NULL) throw CiftiException("write called on unopened ZFileImpl");//shouldn't happen
293 int64_t totalWritten = 0;
294 while (totalWritten < count)
295 {
296 int64_t iterSize = min(count - totalWritten, CHUNK_SIZE);
297 int writeret = gzwrite(m_zfile, ((const char*)dataIn) + totalWritten, iterSize);
298 if (writeret < 1) break;//0 or -1 indicate eof or error
299 totalWritten += writeret;
300 }
301 if (totalWritten != count) throw CiftiException("failed to write to compressed file '" + m_fileName + "'");
302 }
303
304 ZFileImpl::~ZFileImpl()
305 {
306 try//throwing from a destructor is a bad idea
307 {
308 close();
309 } catch (const CiftiException& e) {
310 cerr << AString_to_std_string(e.whatString()) << endl;
311 } catch (exception& e) {
312 cerr << e.what() << endl;
313 } catch (...) {
314 cerr << AString_to_std_string("caught unknown exception type while closing compressed file '" + m_fileName + "'") << endl;
315 }
316 }
317 #endif //ZLIB_VERSION
318
319 #ifdef CIFTILIB_USE_QT
320
321 void QFileImpl::open(const AString& filename, const BinaryFile::OpenMode& opmode)
322 {
323 close();//don't need to, but just because
324 m_fileName = filename;
325 QIODevice::OpenMode mode = QIODevice::NotOpen;//means 0
326 if (opmode & BinaryFile::READ) mode |= QIODevice::ReadOnly;
327 if (opmode & BinaryFile::WRITE) mode |= QIODevice::WriteOnly;
328 if (opmode & BinaryFile::TRUNCATE) mode |= QIODevice::Truncate;//expect QFile to recognize silliness like TRUNCATE by itself
329 m_file.setFileName(filename);
330 if (!m_file.open(mode))
331 {
332 if (!m_file.exists())
333 {
334 if (!(opmode & BinaryFile::TRUNCATE))
335 {
336 throw CiftiException("failed to open file '" + filename + "', file does not exist, or folder permissions prevent seeing it");
337 } else {//m_file.error() doesn't help identify this case, see below
338 throw CiftiException("failed to open file '" + filename + "', unable to create file");
339 }
340 }
341 switch (m_file.error())
342 {
343 case QFile::ResourceError://on linux at least, it never gives another code besides the unhelpful OpenError
344 throw CiftiException("failed to open file '" + filename + "', too many open files");
345 default:
346 throw CiftiException("failed to open file '" + filename + "'");
347 }
348 }
349 }
350
351 void QFileImpl::close()
352 {
353 m_file.close();
354 }
355
356 void QFileImpl::read(void* dataOut, const int64_t& count, int64_t* numRead)
357 {
358 int64_t total = 0;
359 int64_t readret = -1;
360 while (total < count)
361 {
362 int64_t maxToRead = min(count - total, CHUNK_SIZE);
363 readret = m_file.read(((char*)dataOut) + total, maxToRead);//QFile chokes on large reads also
364 if (readret < 1) break;//0 or -1 means error or eof
365 total += readret;
366 }
367 if (numRead == NULL)
368 {
369 if (total != count)
370 {
371 if (readret < 0) throw CiftiException("error while reading file '" + m_fileName + "'");
372 throw CiftiException("premature end of file in '" + m_fileName + "'");
373 }
374 } else {
375 *numRead = total;
376 }
377 }
378
379 void QFileImpl::seek(const int64_t& position)
380 {
381 if (!m_file.seek(position)) throw CiftiException("seek failed in file '" + m_fileName + "'");
382 }
383
384 int64_t QFileImpl::pos()
385 {
386 return m_file.pos();
387 }
388
389 void QFileImpl::write(const void* dataIn, const int64_t& count)
390 {
391 int64_t total = 0;
392 int64_t writeret = -1;
393 while (total < count)
394 {
395 int64_t maxToWrite = min(count - total, CHUNK_SIZE);
396 writeret = m_file.write((const char*)dataIn, maxToWrite);//QFile probably also chokes on large writes
397 if (writeret < 1) break;//0 or -1 means error or eof
398 total += writeret;
399 }
400 if (total != count) throw CiftiException("failed to write to file '" + m_fileName + "'");
401 }
402
403 #else //CIFTILIB_USE_QT
404
405 void StrFileImpl::open(const AString& filename, const BinaryFile::OpenMode& opmode)
406 {
407 close();
408 m_fileName = filename;
409 const char* mode = NULL;
410 switch (opmode)
411 {
412 case BinaryFile::READ:
413 mode = "rb";
414 break;
415 case BinaryFile::READ_WRITE:
416 mode = "r+b";
417 break;
418 case BinaryFile::WRITE_TRUNCATE:
419 mode = "wb";
420 break;
421 case BinaryFile::READ_WRITE_TRUNCATE:
422 mode = "w+b";
423 break;
424 default:
425 throw CiftiException("unsupported open mode in StrFileImpl");
426 }
427 errno = 0;
428 m_file = fopen(ASTRING_TO_CSTR(filename), mode);
429 int save_err = errno;
430 if (m_file == NULL)
431 {
432 if (!boost::filesystem::exists(AString_to_std_string(filename)))
433 {
434 if (!(opmode & BinaryFile::TRUNCATE))
435 {
436 throw CiftiException("failed to open file '" + filename + "', file does not exist, or folder permissions prevent seeing it");
437 } else {
438 throw CiftiException("failed to open file '" + filename + "', unable to create file");
439 }
440 }
441 switch (save_err)
442 {
443 case EMFILE:
444 case ENFILE:
445 throw CiftiException("failed to open file '" + filename + "', too many open files");
446 default:
447 throw CiftiException("failed to open file '" + filename + "'");
448 }
449 }
450 m_curPos = 0;
451 }
452
453 void StrFileImpl::close()
454 {
455 if (m_file == NULL) return;
456 int ret = fclose(m_file);
457 m_file = NULL;
458 m_curPos = -1;
459 if (ret != 0) throw CiftiException("error closing file '" + m_fileName + "'");
460 }
461
462 void StrFileImpl::read(void* dataOut, const int64_t& count, int64_t* numRead)
463 {
464 if (m_file == NULL) throw CiftiException("read called on unopened StrFileImpl");//shouldn't happen
465 int64_t readret = fread(dataOut, 1, count, m_file);//expect fread to not have read size limitations comapred to memory
466 m_curPos += readret;//the item size is 1
467 CiftiAssert(m_curPos == ftello(m_file));//double check it in debug, ftello is fast on linux at least
468 if (numRead == NULL)
469 {
470 if (readret != count)
471 {
472 if (feof(m_file)) throw CiftiException("premature end of file in file '" + m_fileName + "'");
473 throw CiftiException("error while reading file '" + m_fileName + "'");
474 }
475 } else {
476 *numRead = readret;
477 }
478 }
479
480 void StrFileImpl::seek(const int64_t& position)
481 {
482 if (m_file == NULL) throw CiftiException("seek called on unopened StrFileImpl");//shouldn't happen
483 if (position == m_curPos) return;//optimization: calling fseeko causes nontrivial system call time, on linux at least
484 int ret = fseeko(m_file, position, SEEK_SET);
485 if (ret != 0) throw CiftiException("seek failed in file '" + m_fileName + "'");
486 m_curPos = position;
487 }
488
489 int64_t StrFileImpl::pos()
490 {
491 if (m_file == NULL) throw CiftiException("pos called on unopened StrFileImpl");//shouldn't happen
492 CiftiAssert(m_curPos == ftello(m_file));//make sure it is right in debug
493 return m_curPos;//we can avoid a call here also
494 }
495
496 void StrFileImpl::write(const void* dataIn, const int64_t& count)
497 {
498 if (m_file == NULL) throw CiftiException("write called on unopened StrFileImpl");//shouldn't happen
499 int64_t writeret = fwrite(dataIn, 1, count, m_file);//expect fwrite to not have write size limitations compared to memory
500 m_curPos += writeret;//the item size is 1
501 CiftiAssert(m_curPos == ftello(m_file));//double check it in debug, ftello is fast on linux at least
502 if (writeret != count) throw CiftiException("failed to write to file '" + m_fileName + "'");
503 }
504
505 StrFileImpl::~StrFileImpl()
506 {
507 try//throwing from a destructor is a bad idea
508 {
509 close();
510 } catch (const CiftiException& e) {
511 cerr << AString_to_std_string(e.whatString()) << endl;
512 } catch (exception& e) {
513 cerr << e.what() << endl;
514 } catch (...) {
515 cerr << AString_to_std_string("caught unknown exception type while closing file '" + m_fileName + "'") << endl;
516 }
517 }
518
519 #endif //CIFTILIB_USE_QT
0 #ifndef __BINARY_FILE_H__
1 #define __BINARY_FILE_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "boost/shared_ptr.hpp"
31
32 #include "AString.h"
33
34 #include <stdint.h>
35
36 namespace cifti {
37
38 //class to hide difference between compressed and standard binary file reading, and to automate error checking (throws if problem)
39 class BinaryFile
40 {
41 public:
42 enum OpenMode
43 {
44 NONE = 0,
45 READ = 1,
46 WRITE = 2,
47 READ_WRITE = 3,//for convenience
48 TRUNCATE = 4,
49 WRITE_TRUNCATE = 6,//ditto
50 READ_WRITE_TRUNCATE = 7//ditto
51 };
52 BinaryFile() { }
53 ///constructor that opens file
54 BinaryFile(const AString& filename, const OpenMode& fileMode = READ);
55 void open(const AString& filename, const OpenMode& opmode = READ);
56 void close();
57 AString getFilename() const;//not a reference because when no file is open, m_impl is NULL
58 bool getOpenForRead();
59 bool getOpenForWrite();
60 void seek(const int64_t& position);
61 int64_t pos();
62 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)
63 void write(const void* dataIn, const int64_t& count);//failure to complete write is always an exception
64 class ImplInterface
65 {
66 protected:
67 AString m_fileName;//filename is tracked here so error messages can be implementation-specific
68 public:
69 virtual void open(const AString& filename, const OpenMode& opmode) = 0;
70 virtual void close() = 0;
71 const AString& getFilename() const { return m_fileName; }
72 virtual void seek(const int64_t& position) = 0;
73 virtual int64_t pos() = 0;
74 virtual void read(void* dataOut, const int64_t& count, int64_t* numRead) = 0;
75 virtual void write(const void* dataIn, const int64_t& count) = 0;
76 virtual ~ImplInterface();
77 };
78 private:
79 boost::shared_ptr<ImplInterface> m_impl;
80 OpenMode m_curMode;//so implementation classes don't have to track it
81 };
82 } //namespace cifti
83
84 #endif //__BINARY_FILE_H__
0 #ifndef __BYTE_SWAPPING_H__
1 #define __BYTE_SWAPPING_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <stdint.h>
31
32 namespace cifti {
33
34 /**
35 * This class contains static methods for byte swapping data, typically used
36 * when reading binary data files.
37 */
38
39 class ByteSwapping {
40 public:
41 template<typename T>
42 static void swap(T& toSwap);
43
44 template<typename T>
45 static void swapArray(T* toSwap, const uint64_t& count);
46
47 inline static bool isBigEndian()
48 {
49 uint16_t test = 1;
50 return (((char*)&test)[0] == 0);
51 }
52
53 };
54
55 template<typename T>
56 void ByteSwapping::swap(T& toSwap)
57 {
58 if (sizeof(T) == 1) return;//we could specialize 1-byte types, but this should optimize out
59 T temp = toSwap;
60 char* from = (char*)&temp;
61 char* to = (char*)&toSwap;
62 for (int i = 0; i < (int)sizeof(T); ++i)
63 {
64 to[i] = from[sizeof(T) - i - 1];
65 }
66 }
67
68 template<typename T>
69 void ByteSwapping::swapArray(T* toSwap, const uint64_t& count)
70 {
71 if (sizeof(T) == 1) return;//ditto
72 for (uint64_t i = 0; i < count; ++i)
73 {
74 swap(toSwap[i]);
75 }
76 }
77
78 }
79
80 #endif // __BYTE_SWAPPING_H__
81
0
1 PROJECT(Common)
2
3 SET(HEADERS
4 AString.h
5 ByteSwapping.h
6 CiftiAssert.h
7 BinaryFile.h
8 CiftiException.h
9 CiftiMutex.h
10 Compact3DLookup.h
11 CompactLookup.h
12 FloatMatrix.h
13 MatrixFunctions.h
14 MathFunctions.h
15 MultiDimArray.h
16 MultiDimIterator.h
17 Vector3D.h
18 VoxelIJK.h
19 XmlAdapter.h
20 )
21
22 SET(SOURCES
23 AString.cxx
24 BinaryFile.cxx
25 CiftiException.cxx
26 FloatMatrix.cxx
27 MathFunctions.cxx
28 Vector3D.cxx
29 XmlAdapter.cxx
30 )
0 #ifndef __CARET_ASSERT_H__
1 #define __CARET_ASSERT_H__
2
3 //some minimal adaptation to cassert
4 #include <cassert>
5
6 #define CiftiAssert(i) assert(i)
7
8 #define CiftiAssertVectorIndex(v, i) assert((i) >= 0 && (i) < static_cast<int64_t>((v).size()))
9
10 #endif //__CARET_ASSERT_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiException.h"
28
29 using namespace cifti;
30
31 /**
32 * Constructor.
33 *
34 */
35 CiftiException::CiftiException()
36 : std::runtime_error("")
37 {
38 }
39
40 /**
41 * Constructor.
42 *
43 * @param s Description of the exception.
44 *
45 */
46 CiftiException::CiftiException(
47 const AString& s)
48 : std::runtime_error(AString_to_std_string(s))
49 {
50 this->exceptionDescription = s;
51 }
52
53 /**
54 * Copy Constructor.
55 * @param e
56 * Exception that is copied.
57 */
58 CiftiException::CiftiException(const CiftiException& e)
59 : std::runtime_error(e)
60 {
61 this->exceptionDescription = e.exceptionDescription;
62 }
63
64 /**
65 * Assignment operator.
66 * @param e
67 * Exception that is copied.
68 * @return
69 * Copy of the exception.
70 */
71 CiftiException&
72 CiftiException::operator=(const CiftiException& e)
73 {
74 if (this != &e) {
75 std::runtime_error::operator=(e);
76 this->exceptionDescription = e.exceptionDescription;
77 }
78
79 return *this;
80 }
81
82 /**
83 * Destructor
84 */
85 CiftiException::~CiftiException() throw()
86 {
87 }
88
89 /**
90 * Get a message describing the exception.
91 * @return A message describing the exception.
92 */
93 AString
94 CiftiException::whatString() const throw()
95 {
96 return this->exceptionDescription;
97 }
0 #ifndef __CIFTI_EXCEPTION_H__
1 #define __CIFTI_EXCEPTION_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "AString.h"
31
32 #include <stdexcept>
33
34 namespace cifti {
35
36 /**
37 * An exception thrown from CiftiLib.
38 */
39 class CiftiException : public std::runtime_error {
40
41 public:
42 CiftiException();
43
44 CiftiException(const AString& s);
45
46 CiftiException(const CiftiException& e);
47
48 CiftiException& operator=(const CiftiException& e);
49
50 virtual ~CiftiException() throw();
51
52 virtual AString whatString() const throw();
53
54 private:
55 /// Description of the exception
56 AString exceptionDescription;
57 };
58
59 } // namespace
60
61 #endif // __CIFTI_EXCEPTION_H__
0 #ifndef __CIFTI_MUTEX_H__
1 #define __CIFTI_MUTEX_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2016, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #ifdef _OPENMP
31 #define __CIFTI_MUTEX_H_HAVE_IMPL__
32
33 #include "omp.h"
34
35 namespace cifti
36 {
37 class CiftiMutex
38 {
39 omp_lock_t m_lock;
40 public:
41 CiftiMutex(const CiftiMutex&) { omp_init_lock(&m_lock); };//allow copy, assign, but make them do nothing other than default construct
42 CiftiMutex& operator=(const CiftiMutex&) { return *this; };
43 CiftiMutex() { omp_init_lock(&m_lock); }
44 ~CiftiMutex() { omp_destroy_lock(&m_lock); }
45 friend class CiftiMutexLocker;
46 };
47
48 class CiftiMutexLocker
49 {
50 CiftiMutex* m_mutex;
51 CiftiMutexLocker();//disallow default construction, assign
52 CiftiMutexLocker& operator=(const CiftiMutexLocker& rhs);
53 public:
54 CiftiMutexLocker(CiftiMutex* mutex) { m_mutex = mutex; omp_set_lock(&(m_mutex->m_lock)); }
55 ~CiftiMutexLocker() { omp_unset_lock(&(m_mutex->m_lock)); }
56 };
57 }
58
59 #else //_OPENMP
60
61 #ifdef CIFTILIB_USE_QT
62 #define __CIFTI_MUTEX_H_HAVE_IMPL__
63
64 #include <QMutex>
65
66 namespace cifti
67 {
68 typedef QMutex CiftiMutex;
69 typedef QMutexLocker CiftiMutexLocker;
70 }
71
72 #endif //CIFTILIB_USE_QT
73
74 #ifdef CIFTILIB_USE_XMLPP
75 #define __CIFTI_MUTEX_H_HAVE_IMPL__
76
77 #include <glibmm/thread.h>
78
79 namespace cifti
80 {
81 typedef Glib::Mutex CiftiMutex;
82
83 //API difference: glib's locker class takes a reference, while QT's takes a pointer
84 class CiftiMutexLocker
85 {
86 CiftiMutex* m_mutex;
87 CiftiMutexLocker();//disallow default construction, assign
88 CiftiMutexLocker& operator=(const CiftiMutexLocker& rhs);
89 public:
90 CiftiMutexLocker(CiftiMutex* mutex) { m_mutex = mutex; m_mutex->lock(); }
91 ~CiftiMutexLocker() { m_mutex->unlock(); }
92 };
93 }
94
95 #endif //CIFTILIB_USE_XMLPP
96
97 #endif //_OPENMP
98
99
100 #ifndef __CIFTI_MUTEX_H_HAVE_IMPL__
101 #error "you must have openmp support, or define either CIFTILIB_USE_QT or CIFTILIB_USE_XMLPP to select what mutex implementation to use"
102 #endif
103
104 #endif //__CIFTI_MUTEX_H__
0 #ifndef __COMPACT_3D_LOOKUP_H__
1 #define __COMPACT_3D_LOOKUP_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CompactLookup.h"
31
32 namespace cifti
33 {
34
35 template <typename T>
36 class Compact3DLookup
37 {
38 CompactLookup<CompactLookup<CompactLookup<T> > > m_lookup;//the whole point of this class is to deal with this ugliness
39 public:
40 ///creates the element if it didn't exist, and returns a reference to it
41 T& at(const int64_t& index1, const int64_t& index2, const int64_t& index3);
42 ///creates the element if it didn't exist, and returns a reference to it
43 T& at(const int64_t index[3]) { return at(index[0], index[1], index[2]); }
44 ///add or overwrite an element in the lookup
45 void insert(const int64_t& index1, const int64_t& index2, const int64_t& index3, const T& value)
46 { at(index1, index2, index3) = value; }
47 ///add or overwrite an element in the lookup
48 void insert(const int64_t index[3], const T& value)
49 { at(index) = value; }
50 ///returns a pointer to the desired element, or NULL if no such element is found
51 T* find(const int64_t& index1, const int64_t& index2, const int64_t& index3);
52 ///returns a pointer to the desired element, or NULL if no such element is found
53 T* find(const int64_t index[3]) { return find(index[0], index[1], index[2]); }
54 ///returns a pointer to the desired element, or NULL if no such element is found
55 const T* find(const int64_t& index1, const int64_t& index2, const int64_t& index3) const;
56 ///returns a pointer to the desired element, or NULL if no such element is found
57 const T* find(const int64_t index[3]) const { return find(index[0], index[1], index[2]); }
58 ///empties the lookup
59 void clear();
60 };
61
62 template<typename T>
63 T& Compact3DLookup<T>::at(const int64_t& index1, const int64_t& index2, const int64_t& index3)
64 {
65 return m_lookup[index3][index2][index1];//a lot of complexity is hidden in those operator[]s
66 }
67
68 template<typename T>
69 T* Compact3DLookup<T>::find(const int64_t& index1, const int64_t& index2, const int64_t& index3)
70 {
71 typename CompactLookup<CompactLookup<CompactLookup<T> > >::iterator iter1 = m_lookup.find(index3);//oh the humanity
72 if (iter1 == m_lookup.end()) return NULL;
73 typename CompactLookup<CompactLookup<T> >::iterator iter2 = iter1->find(index2);
74 if (iter2 == iter1->end()) return NULL;
75 typename CompactLookup<T>::iterator iter3 = iter2->find(index1);
76 if (iter3 == iter2->end()) return NULL;
77 return &(*iter3);
78 }
79
80 template <typename T>
81 const T* Compact3DLookup<T>::find(const int64_t& index1, const int64_t& index2, const int64_t& index3) const
82 {
83 typename CompactLookup<CompactLookup<CompactLookup<T> > >::const_iterator iter1 = m_lookup.find(index3);
84 if (iter1 == m_lookup.end()) return NULL;
85 typename CompactLookup<CompactLookup<T> >::const_iterator iter2 = iter1->find(index2);
86 if (iter2 == iter1->end()) return NULL;
87 typename CompactLookup<T>::const_iterator iter3 = iter2->find(index1);
88 if (iter3 == iter2->end()) return NULL;
89 return &(*iter3);
90 }
91
92 template <typename T>
93 void Compact3DLookup<T>::clear()
94 {
95 m_lookup.clear();
96 }
97
98 }
99
100 #endif //__COMPACT_3D_LOOKUP_H__
0 #ifndef __COMPACT_LOOKUP_H__
1 #define __COMPACT_LOOKUP_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiAssert.h"
31 #include "stdint.h"
32 #include <vector>
33
34 namespace cifti
35 {
36
37 template <typename T>
38 class CompactLookup
39 {
40 struct Chunk
41 {
42 int64_t start;
43 std::vector<T> elements;
44 };
45 std::vector<Chunk> m_chunks;
46 public:
47 class iterator
48 {
49 CompactLookup<T>& m_container;
50 std::size_t m_chunk;
51 int64_t m_elem;
52 iterator(CompactLookup<T>& container, std::size_t chunk, int64_t elem) : m_container(container), m_chunk(chunk), m_elem(elem) { }
53 public:
54 bool operator==(const iterator& rhs) const
55 {
56 if (&m_container != &(rhs.m_container)) return false;
57 if (m_chunk != rhs.m_chunk) return false;
58 if (m_elem != rhs.m_elem) return false;
59 return true;
60 }
61 T& operator*()
62 {
63 CiftiAssert(m_chunk < m_container.m_chunks.size());
64 CiftiAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size());
65 return m_container.m_chunks[m_chunk].elements[m_elem];
66 }
67 T* operator->()
68 {
69 CiftiAssert(m_chunk < m_container.m_chunks.size());
70 CiftiAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size());
71 return &(m_container.m_chunks[m_chunk].elements[m_elem]);
72 }
73 friend class CompactLookup<T>;
74 };
75 class const_iterator
76 {
77 const CompactLookup<T>& m_container;
78 std::size_t m_chunk;
79 int64_t m_elem;
80 const_iterator(const CompactLookup<T>& container, std::size_t chunk, std::size_t elem) : m_container(container), m_chunk(chunk), m_elem(elem) { }
81 public:
82 bool operator==(const const_iterator& rhs) const
83 {
84 if (&m_container != &(rhs.m_container)) return false;
85 if (m_chunk != rhs.m_chunk) return false;
86 if (m_elem != rhs.m_elem) return false;
87 return true;
88 }
89 const T& operator*()
90 {
91 CiftiAssert(m_chunk < m_container.m_chunks.size());
92 CiftiAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size());
93 return m_container.m_chunks[m_chunk].elements[m_elem];
94 }
95 const T* operator->()
96 {
97 CiftiAssert(m_chunk < m_container.m_chunks.size());
98 CiftiAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size());
99 return &(m_container.m_chunks[m_chunk].elements[m_elem]);
100 }
101 friend class CompactLookup<T>;
102 };
103 ///creates the element if it didn't exist, and returns a reference to it
104 T& operator[](const int64_t& index);
105 ///returns an iterator pointing to the desired element, or one equal to end() if no such element is found
106 iterator find(const int64_t& index);
107 ///returns a const_iterator pointing to the desired element, or one equal to end() if no such element is found
108 const_iterator find(const int64_t& index) const;
109 iterator end();
110 const_iterator end() const;
111 ///empties the lookup
112 void clear();
113 };
114
115 template <typename T>
116 T& CompactLookup<T>::operator[](const int64_t& index)
117 {
118 std::size_t numChunks = m_chunks.size();
119 std::size_t low = 0, high = numChunks, guess;//NOTE: low is 0 because size_t is unsigned, really means -1
120 bool attach_low = false, attach_high = false;
121 while (low < high)//bisection search for the chunk with the largest start value not greater than
122 {
123 guess = (low + high - 1) / 2;//which is why we subtract 1 here
124 CiftiAssert(guess < m_chunks.size());
125 if (m_chunks[guess].start > index)
126 {
127 high = guess;
128 } else {
129 low = guess + 1;
130 }
131 }//NOTE: low == high after loop ends
132 if (high > 0 && m_chunks[high - 1].start + (int64_t)(m_chunks[high - 1].elements.size()) > index)//element exists, return it
133 {
134 CiftiAssertVectorIndex(m_chunks[high -1].elements, index - m_chunks[high - 1].start);
135 return m_chunks[high - 1].elements[index - m_chunks[high - 1].start];
136 }
137 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
138 if (high < numChunks && m_chunks[high].start == index + 1) attach_high = true;//index is 1 before the next range
139 if (attach_low)
140 {
141 std::vector<T>& lowvec = m_chunks[high - 1].elements;
142 std::size_t retIndex = lowvec.size();
143 lowvec.push_back(T());
144 if (attach_high)
145 {
146 std::vector<T>& highvec = m_chunks[high].elements;
147 lowvec.insert(lowvec.end(), highvec.begin(), highvec.end());
148 m_chunks.erase(m_chunks.begin() + high);
149 }
150 return lowvec[retIndex];
151 } else {
152 if (attach_high)
153 {
154 std::vector<T>& highvec = m_chunks[high].elements;
155 highvec.insert(highvec.begin(), T());//add a new element to the start of the vector (yes, this could be slow)
156 m_chunks[high].start = index;//and change the start value of the chunk
157 return highvec[0];
158 } else {
159 m_chunks.insert(m_chunks.begin() + high, Chunk());
160 m_chunks[high].start = index;
161 m_chunks[high].elements.push_back(T());
162 return m_chunks[high].elements[0];
163 }
164 }
165 }
166
167 template <typename T>
168 typename CompactLookup<T>::iterator CompactLookup<T>::find(const int64_t& index)
169 {
170 std::size_t numChunks = m_chunks.size();
171 std::size_t low = 0, high = numChunks, guess;//NOTE: low is 0 because size_t is unsigned, really means -1
172 while (low < high)//bisection search for the chunk with the largest start value not greater than
173 {
174 guess = (low + high - 1) / 2;//which is why we subtract 1 here
175 CiftiAssert(guess < m_chunks.size());
176 if (m_chunks[guess].start > index)
177 {
178 high = guess;
179 } else {
180 low = guess + 1;
181 }
182 }//NOTE: low == high after loop ends
183 if (high > 0 && m_chunks[high - 1].start + (int64_t)(m_chunks[high - 1].elements.size()) > index)//element exists, return it
184 {
185 std::size_t outIndex = index - m_chunks[high - 1].start;
186 CiftiAssert(outIndex < m_chunks[high - 1].elements.size());
187 return iterator(*this, high - 1, outIndex);
188 }
189 return end();
190 }
191
192 template <typename T>
193 typename CompactLookup<T>::const_iterator CompactLookup<T>::find(const int64_t& index) const
194 {
195 std::size_t numChunks = m_chunks.size();
196 std::size_t low = 0, high = numChunks, guess;//NOTE: low is 0 because size_t is unsigned, really means -1
197 while (low < high)//bisection search for the chunk with the largest start value not greater than
198 {
199 guess = (low + high - 1) / 2;//which is why we subtract 1 here
200 CiftiAssert(guess < m_chunks.size());
201 if (m_chunks[guess].start > index)
202 {
203 high = guess;
204 } else {
205 low = guess + 1;
206 }
207 }//NOTE: low == high after loop ends
208 if (high > 0 && m_chunks[high - 1].start + (int64_t)(m_chunks[high - 1].elements.size()) > index)//element exists, return it
209 {
210 std::size_t outIndex = index - m_chunks[high - 1].start;
211 CiftiAssert(outIndex < m_chunks[high - 1].elements.size());
212 return const_iterator(*this, high - 1, outIndex);
213 }
214 return end();
215 }
216
217 template <typename T>
218 typename CompactLookup<T>::iterator CompactLookup<T>::end()
219 {
220 return iterator(*this, 0, -1);
221 }
222
223 template <typename T>
224 typename CompactLookup<T>::const_iterator CompactLookup<T>::end() const
225 {
226 return const_iterator(*this, 0, -1);
227 }
228
229 template <typename T>
230 void CompactLookup<T>::clear()
231 {
232 m_chunks.clear();
233 }
234 }
235
236 #endif //__COMPACT_LOOKUP_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiAssert.h"
28 #include "CiftiException.h"
29 #include "FloatMatrix.h"
30 #include "MatrixFunctions.h"
31
32 using namespace cifti;
33 using namespace std;
34
35 bool FloatMatrix::checkDimensions() const
36 {
37 uint64_t rows = m_matrix.size(), cols;
38 if (rows == 0) return true;//treat it as fine for now
39 cols = m_matrix[0].size();
40 for (uint64_t i = 1; i < rows; ++i)
41 {
42 if (m_matrix[i].size() != cols)
43 {
44 return false;
45 }
46 }
47 return true;
48 }
49
50 FloatMatrix::FloatMatrix(const vector<vector<float> >& matrixIn)
51 {
52 m_matrix = matrixIn;
53 CiftiAssert(checkDimensions());
54 }
55
56 FloatMatrix::FloatMatrix(const int64_t& rows, const int64_t& cols)
57 {
58 resize(rows, cols, true);
59 }
60
61 bool FloatMatrix::operator!=(const FloatMatrix& right) const
62 {
63 return !(*this == right);
64 }
65
66 FloatMatrix FloatMatrix::operator*(const FloatMatrix& right) const
67 {
68 FloatMatrix ret;
69 MatrixFunctions::multiply<float, float, float, double>(m_matrix, right.m_matrix, ret.m_matrix);
70 return ret;
71 }
72
73 FloatMatrix& FloatMatrix::operator*=(const FloatMatrix& right)
74 {
75 MatrixFunctions::multiply<float, float, float, double>(m_matrix, right.m_matrix, m_matrix);//would need a copy anyway, so let it make the copy internally
76 return *this;
77 }
78
79 FloatMatrix FloatMatrix::concatHoriz(const FloatMatrix& right) const
80 {
81 FloatMatrix ret;
82 MatrixFunctions::horizCat(m_matrix, right.m_matrix, ret.m_matrix);
83 return ret;
84 }
85
86 FloatMatrix FloatMatrix::concatVert(const FloatMatrix& bottom) const
87 {
88 FloatMatrix ret;
89 MatrixFunctions::vertCat(m_matrix, bottom.m_matrix, ret.m_matrix);
90 return ret;
91 }
92
93 FloatMatrix FloatMatrix::getRange(const int64_t firstRow, const int64_t afterLastRow, const int64_t firstCol, const int64_t afterLastCol) const
94 {
95 FloatMatrix ret;
96 MatrixFunctions::getChunk(firstRow, afterLastRow, firstCol, afterLastCol, m_matrix, ret.m_matrix);
97 return ret;
98 }
99
100 FloatMatrix FloatMatrix::identity(const int64_t rows)
101 {
102 FloatMatrix ret;
103 MatrixFunctions::identity(rows, ret.m_matrix);
104 return ret;
105 }
106
107 FloatMatrix FloatMatrix::inverse() const
108 {
109 FloatMatrix ret;
110 MatrixFunctions::inverse(m_matrix, ret.m_matrix);
111 return ret;
112 }
113
114 FloatMatrix& FloatMatrix::operator*=(const float& right)
115 {
116 MatrixFunctions::multiply(m_matrix, right, m_matrix);//internally makes a copy
117 return *this;
118 }
119
120 FloatMatrix FloatMatrix::operator+(const FloatMatrix& right) const
121 {
122 FloatMatrix ret;
123 MatrixFunctions::add(m_matrix, right.m_matrix, ret.m_matrix);
124 return ret;
125 }
126
127 FloatMatrix& FloatMatrix::operator+=(const FloatMatrix& right)
128 {
129 MatrixFunctions::add(m_matrix, right.m_matrix, m_matrix);
130 return *this;
131 }
132
133 FloatMatrix& FloatMatrix::operator+=(const float& right)
134 {
135 MatrixFunctions::add(m_matrix, right, m_matrix);
136 return *this;
137 }
138
139 FloatMatrix FloatMatrix::operator-(const FloatMatrix& right) const
140 {
141 FloatMatrix ret;
142 MatrixFunctions::subtract(m_matrix, right.m_matrix, ret.m_matrix);
143 return ret;
144 }
145
146 FloatMatrix& FloatMatrix::operator-=(const FloatMatrix& right)
147 {
148 MatrixFunctions::subtract(m_matrix, right.m_matrix, m_matrix);
149 return *this;
150 }
151
152 FloatMatrix& FloatMatrix::operator-=(const float& right)
153 {
154 MatrixFunctions::add(m_matrix, (-right), m_matrix);
155 return *this;
156 }
157
158 FloatMatrix& FloatMatrix::operator/=(const float& right)
159 {
160 return ((*this) *= 1.0f / right);
161 }
162
163 bool FloatMatrix::operator==(const FloatMatrix& right) const
164 {
165 if (this == &right)
166 {
167 return true;//short circuit true on pointer equivalence
168 }
169 int64_t i, j, rows = (int64_t)m_matrix.size(), cols;
170 if (rows != (int64_t)right.m_matrix.size())
171 {
172 return false;
173 }
174 if (rows == 0)
175 {
176 return true;//don't try to get the second dimension
177 }
178 cols = (int64_t)m_matrix[0].size();
179 if (cols != (int64_t)right.m_matrix[0].size())
180 {
181 return false;
182 }
183 for (i = 0; i < rows; ++i)
184 {
185 for (j = 0; j < cols; ++j)
186 {
187 if (m_matrix[i][j] != right.m_matrix[i][j])
188 {
189 return false;
190 }
191 }
192 }
193 return true;
194 }
195
196 void FloatMatrix::getDimensions(int64_t& rows, int64_t& cols) const
197 {
198 rows = (int64_t)m_matrix.size();
199 if (rows == 0)
200 {
201 cols = 0;
202 } else {
203 cols = (int64_t)m_matrix[0].size();
204 }
205 }
206
207 FloatMatrixRowRef FloatMatrix::operator[](const int64_t& index)
208 {
209 CiftiAssert(index > -1 && index < (int64_t)m_matrix.size());
210 FloatMatrixRowRef ret(m_matrix[index]);
211 return ret;
212 }
213
214 ConstFloatMatrixRowRef FloatMatrix::operator[](const int64_t& index) const
215 {
216 CiftiAssert(index > -1 && index < (int64_t)m_matrix.size());
217 ConstFloatMatrixRowRef ret(m_matrix[index]);
218 return ret;
219 }
220
221 FloatMatrix FloatMatrix::reducedRowEchelon() const
222 {
223 FloatMatrix ret(*this);
224 MatrixFunctions::rref(ret.m_matrix);
225 return ret;
226 }
227
228 void FloatMatrix::resize(const int64_t rows, const int64_t cols, const bool destructive)
229 {
230 MatrixFunctions::resize(rows, cols, m_matrix, destructive);
231 }
232
233 FloatMatrix FloatMatrix::transpose() const
234 {
235 FloatMatrix ret;
236 MatrixFunctions::transpose(m_matrix, ret.m_matrix);
237 return ret;
238 }
239
240 FloatMatrix FloatMatrix::zeros(const int64_t rows, const int64_t cols)
241 {
242 FloatMatrix ret;
243 MatrixFunctions::zeros(rows, cols, ret.m_matrix);
244 return ret;
245 }
246
247 FloatMatrix FloatMatrix::ones(const int64_t rows, const int64_t cols)
248 {
249 FloatMatrix ret;
250 MatrixFunctions::ones(rows, cols, ret.m_matrix);
251 return ret;
252 }
253
254 const vector<vector<float> >& FloatMatrix::getMatrix() const
255 {
256 return m_matrix;
257 }
258
259 void FloatMatrix::getAffineVectors(Vector3D& xvec, Vector3D& yvec, Vector3D& zvec, Vector3D& offset) const
260 {
261 if (m_matrix.size() < 3 || m_matrix.size() > 4 || m_matrix[0].size() != 4)
262 {
263 throw CiftiException("getAffineVectors called on incorrectly sized matrix");
264 }
265 xvec[0] = m_matrix[0][0]; xvec[1] = m_matrix[1][0]; xvec[2] = m_matrix[2][0];
266 yvec[0] = m_matrix[0][1]; yvec[1] = m_matrix[1][1]; yvec[2] = m_matrix[2][1];
267 zvec[0] = m_matrix[0][2]; zvec[1] = m_matrix[1][2]; zvec[2] = m_matrix[2][2];
268 offset[0] = m_matrix[0][3]; offset[1] = m_matrix[1][3]; offset[2] = m_matrix[2][3];
269 }
270
271 FloatMatrix FloatMatrix::operator-() const
272 {
273 int64_t rows, cols;
274 getDimensions(rows, cols);
275 FloatMatrix ret = zeros(rows, cols);
276 ret -= *this;
277 return ret;
278 }
279
280 FloatMatrixRowRef::FloatMatrixRowRef(vector<float>& therow) : m_row(therow)
281 {
282 }
283
284 FloatMatrixRowRef& FloatMatrixRowRef::operator=(const FloatMatrixRowRef& right)
285 {
286 if (&m_row == &(right.m_row))
287 {//just in case vector isn't smart enough to check self assignment
288 return *this;
289 }
290 CiftiAssert(m_row.size() == right.m_row.size());//maybe this should be an exception, not an assertion?
291 m_row = right.m_row;
292 return *this;
293 }
294
295 FloatMatrixRowRef& FloatMatrixRowRef::operator=(const float& right)
296 {
297 for (int64_t i = 0; i < (int64_t)m_row.size(); ++i)
298 {
299 m_row[i] = right;
300 }
301 return *this;
302 }
303
304 float& FloatMatrixRowRef::operator[](const int64_t& index)
305 {
306 CiftiAssert(index > -1 && index < (int64_t)m_row.size());//instead of segfaulting, explicitly check in debug
307 return m_row[index];
308 }
309
310 FloatMatrixRowRef::FloatMatrixRowRef(FloatMatrixRowRef& right) : m_row(right.m_row)
311 {
312 }
313
314 FloatMatrixRowRef& FloatMatrixRowRef::operator=(const ConstFloatMatrixRowRef& right)
315 {
316 if (&m_row == &(right.m_row))
317 {//just in case vector isn't smart enough to check self assignment
318 return *this;
319 }
320 CiftiAssert(m_row.size() == right.m_row.size());
321 m_row = right.m_row;
322 return *this;
323 }
324
325 const float& ConstFloatMatrixRowRef::operator[](const int64_t& index)
326 {
327 CiftiAssert(index > -1 && index < (int64_t)m_row.size());//instead of segfaulting, explicitly check in debug
328 return m_row[index];
329 }
330
331 ConstFloatMatrixRowRef::ConstFloatMatrixRowRef(const ConstFloatMatrixRowRef& right) : m_row(right.m_row)
332 {
333 }
334
335 ConstFloatMatrixRowRef::ConstFloatMatrixRowRef(const vector<float>& therow) : m_row(therow)
336 {
337 }
0 #ifndef __FLOAT_MATRIX_H__
1 #define __FLOAT_MATRIX_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <vector>
31 #include "stdint.h"
32 #include "Vector3D.h"
33
34 namespace cifti {
35
36 class ConstFloatMatrixRowRef
37 {//needed to do [][] on a const FloatMatrix
38 const std::vector<float>& m_row;
39 ConstFloatMatrixRowRef();//disallow default construction, this contains a reference
40 public:
41 ConstFloatMatrixRowRef(const ConstFloatMatrixRowRef& right);//copy constructor
42 ConstFloatMatrixRowRef(const std::vector<float>& therow);
43 const float& operator[](const int64_t& index);//access element
44 friend class FloatMatrixRowRef;//so it can check if it points to the same row
45 };
46
47 class FloatMatrixRowRef
48 {//needed to ensure some joker doesn't call mymatrix[1].resize();, while still allowing mymatrix[1][2] = 5; and mymatrix[1] = mymatrix[2];
49 std::vector<float>& m_row;
50 FloatMatrixRowRef();//disallow default construction, this contains a reference
51 public:
52 FloatMatrixRowRef(FloatMatrixRowRef& right);//copy constructor
53 FloatMatrixRowRef(std::vector<float>& therow);
54 FloatMatrixRowRef& operator=(const FloatMatrixRowRef& right);//NOTE: copy row contents!
55 FloatMatrixRowRef& operator=(const ConstFloatMatrixRowRef& right);//NOTE: copy row contents!
56 FloatMatrixRowRef& operator=(const float& right);//NOTE: set all row values!
57 float& operator[](const int64_t& index);//access element
58 };
59
60 ///class for using single precision matrices (insulates other code from the MatrixFunctions templated header)
61 ///errors will result in a matrix of size 0x0, or an assertion failure if the underlying vector<vector> isn't rectangular
62 class FloatMatrix
63 {
64 std::vector<std::vector<float> > m_matrix;
65 bool checkDimensions() const;//put this inside asserts at the end of functions
66 public:
67 FloatMatrix() { };//to make the compiler happy
68 ///construct from a simple vector<vector<float> >
69 FloatMatrix(const std::vector<std::vector<float> >& matrixIn);
70 ///construct uninitialized with given size
71 FloatMatrix(const int64_t& rows, const int64_t& cols);
72 FloatMatrixRowRef operator[](const int64_t& index);//allow direct indexing to rows
73 ConstFloatMatrixRowRef operator[](const int64_t& index) const;//allow direct indexing to rows while const
74 FloatMatrix& operator+=(const FloatMatrix& right);//add to
75 FloatMatrix& operator-=(const FloatMatrix& right);//subtract from
76 FloatMatrix& operator*=(const FloatMatrix& right);//multiply by
77 FloatMatrix& operator+=(const float& right);//add scalar to
78 FloatMatrix& operator-=(const float& right);//subtract scalar from
79 FloatMatrix& operator*=(const float& right);//multiply by scalar
80 FloatMatrix& operator/=(const float& right);//divide by scalar
81 FloatMatrix operator+(const FloatMatrix& right) const;//add
82 FloatMatrix operator-(const FloatMatrix& right) const;//subtract
83 FloatMatrix operator-() const;//negate
84 FloatMatrix operator*(const FloatMatrix& right) const;//multiply
85 bool operator==(const FloatMatrix& right) const;//compare
86 bool operator!=(const FloatMatrix& right) const;//anti-compare
87 ///return the inverse
88 FloatMatrix inverse() const;
89 ///return the reduced row echelon form
90 FloatMatrix reducedRowEchelon() const;
91 ///return the transpose
92 FloatMatrix transpose() const;
93 ///resize the matrix - keeps contents within bounds unless destructive is true (destructive is faster)
94 void resize(const int64_t rows, const int64_t cols, const bool destructive = false);
95 ///return a matrix of zeros
96 static FloatMatrix zeros(const int64_t rows, const int64_t cols);
97 ///return a matrix of ones
98 static FloatMatrix ones(const int64_t rows, const int64_t cols);
99 ///return square identity matrix
100 static FloatMatrix identity(const int64_t rows);
101 ///get the range of values from first until one before afterLast, as a new matrix
102 FloatMatrix getRange(const int64_t firstRow, const int64_t afterLastRow, const int64_t firstCol, const int64_t afterLastCol) const;
103 ///return a matrix formed by concatenating right to the right of this
104 FloatMatrix concatHoriz(const FloatMatrix& right) const;
105 ///returns a matrix formed by concatenating bottom to the bottom of this
106 FloatMatrix concatVert(const FloatMatrix& bottom) const;
107 ///get the dimensions
108 void getDimensions(int64_t& rows, int64_t& cols) const;
109 ///get the matrix as a vector<vector>
110 const std::vector<std::vector<float> >& getMatrix() const;
111 ///separate 3x4 or 4x4 into Vector3Ds, throw on wrong dimensions
112 void getAffineVectors(Vector3D& xvec, Vector3D& yvec, Vector3D& zvec, Vector3D& offset) const;
113 ///get number of rows
114 int64_t getNumberOfRows() { return (int64_t)m_matrix.size(); }
115 ///get number of columns
116 int64_t getNumberOfColumns()
117 {
118 if (m_matrix.size() == 0) return 0;
119 return (int64_t)m_matrix[0].size();
120 }
121 };
122
123 }
124
125 #endif //__FLOAT_MATRIX_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <algorithm>
28 #include <cmath>
29
30 #include "MathFunctions.h"
31
32 using namespace cifti;
33
34 /**
35 * Calulate the number of combinations for choosing "k" elements
36 * from a total of "n" elements.
37 *
38 * Formula: [n!/(k!*(n-k!))
39 * If k > (n-k): [n(n-1)(n-2)...(k+1)] / (n-k)!
40 * If k < (n-k): [n(n-1)(n-2)...(n-k+1)] / k!
41 *
42 * @param n - total number of elements.
43 * @param k - number of elements to choose.
44 * @return - number of combinations.
45 *
46 */
47 int64_t
48 MathFunctions::combinations(
49 const int64_t n,
50 const int64_t k)
51 {
52 int64_t denominator = 1;
53 int64_t nmk = n - k;
54 int64_t iStart = 1;
55
56 if (k > nmk) {
57 iStart = k + 1;
58 denominator = MathFunctions::factorial(nmk);
59 }
60 else {
61 iStart = nmk + 1;
62 denominator = MathFunctions::factorial(k);
63 }
64 int64_t numerator = 1;
65 for (int64_t i = iStart; i <= n; i++) {
66 numerator *= i;
67 }
68
69 int64_t numCombosLong = numerator / denominator;
70 int64_t numCombos = (int)numCombosLong;
71
72 return numCombos;
73 }
74
75 /**
76 * Calulate the number of permuations for choosing "k" elements
77 * from a total of "n" elements.
78 *
79 * Formula: [n!/(n-m)!] = n(n-1)(n-2)...(n-m+1).
80 *
81 * @param n - total number of elements.
82 * @param k - number of elements to choose.
83 * @return - number of combinations.
84 *
85 */
86 int64_t
87 MathFunctions::permutations(
88 const int64_t n,
89 const int64_t k)
90 {
91 int64_t iStart = n - k + 1;
92 int numPerms = 1;
93 for (int i = iStart; i <= n; i++) {
94 numPerms *= i;
95 }
96
97 return numPerms;
98 }
99
100 /**
101 * Calculate the factorial for a number.
102 *
103 * @param n - the number.
104 * @return - its factiorial
105 *
106 */
107 int64_t
108 MathFunctions::factorial(const int64_t n)
109 {
110 int64_t num = 1;
111
112 for (int64_t i = 1; i <= n; i++) {
113 num *= i;
114 }
115
116 return num;
117 }
118
119 /**
120 * Compute a normal vector from three vertices and make it a unit vector.
121 *
122 * @param v1 the first vertex, an array of three floats.
123 * @param v2 the first vertex, an array of three floats.
124 * @param v3 the first vertex, an array of three floats.
125 * @param normalVectorOut A three-dimensional array passed into which
126 * the normal vector is loaded.
127 * @return true if vector is valid (non-zero length).
128 *
129 */
130 bool
131 MathFunctions::normalVector(
132 const float v1[3],
133 const float v2[3],
134 const float v3[3],
135 float normalVectorOut[3])
136 {
137 /*
138 * DOUBLE PRECISION is needed when points are a small or sliver triangle.
139 */
140 double a0 = v3[0] - v2[0];
141 double a1 = v3[1] - v2[1];
142 double a2 = v3[2] - v2[2];
143
144 double b0 = v1[0] - v2[0];
145 double b1 = v1[1] - v2[1];
146 double b2 = v1[2] - v2[2];
147
148 double nv0 = (a1 * b2 - a2 * b1);
149 double nv1 = (a2 * b0 - a0 * b2);
150 double nv2 = (a0 * b1 - a1 * b0);
151
152 double length = std::sqrt(nv0*nv0 + nv1*nv1 + nv2*nv2);
153 bool valid = false;
154 if (length != 0.0) {
155 nv0 /= length;
156 nv1 /= length;
157 nv2 /= length;
158 valid = true;
159 }
160
161 normalVectorOut[0] = (float)nv0;
162 normalVectorOut[1] = (float)nv1;
163 normalVectorOut[2] = (float)nv2;
164
165 return valid;
166 }
167
168 /**
169 * Compute a normal vector from three vertices and make it a unit vector.
170 *
171 * @param v1 the first vertex, an array of three floats.
172 * @param v2 the first vertex, an array of three floats.
173 * @param v3 the first vertex, an array of three floats.
174 * @param normalVectorOut A three-dimensional array passed into which
175 * the normal vector is loaded.
176 * @return true if vector is valid (non-zero length).
177 *
178 */
179 bool
180 MathFunctions::normalVector(
181 const double v1[3],
182 const double v2[3],
183 const double v3[3],
184 double normalVectorOut[3])
185 {
186 double a0 = v3[0] - v2[0];
187 double a1 = v3[1] - v2[1];
188 double a2 = v3[2] - v2[2];
189
190 double b0 = v1[0] - v2[0];
191 double b1 = v1[1] - v2[1];
192 double b2 = v1[2] - v2[2];
193
194 double nv0 = (a1 * b2 - a2 * b1);
195 double nv1 = (a2 * b0 - a0 * b2);
196 double nv2 = (a0 * b1 - a1 * b0);
197
198 double length = std::sqrt(nv0*nv0 + nv1*nv1 + nv2*nv2);
199 bool valid = false;
200 if (length != 0.0) {
201 nv0 /= length;
202 nv1 /= length;
203 nv2 /= length;
204 valid = true;
205 }
206
207 normalVectorOut[0] = nv0;
208 normalVectorOut[1] = nv1;
209 normalVectorOut[2] = nv2;
210
211 return valid;
212 }
213
214 /**
215 * Compute a normal vector from three vertices and but the returned
216 * vector IS NOT a unit vector.
217 *
218 * @param v1 the first vertex, an array of three floats.
219 * @param v2 the first vertex, an array of three floats.
220 * @param v3 the first vertex, an array of three floats.
221 * @return The normal vector, an array of three floats.
222 *
223 */
224 void
225 MathFunctions::normalVectorDirection(
226 const float v1[3],
227 const float v2[3],
228 const float v3[3],
229 float directionOut[3])
230 {
231 float a[] = {
232 v3[0] - v2[0],
233 v3[1] - v2[1],
234 v3[2] - v2[2]
235 };
236 float b[] = {
237 v1[0] - v2[0],
238 v1[1] - v2[1],
239 v1[2] - v2[2]
240 };
241
242 directionOut[0] = (a[1] * b[2] - a[2] * b[1]);
243 directionOut[1] = (a[2] * b[0] - a[0] * b[2]);
244 directionOut[2] = (a[0] * b[1] - a[1] * b[0]);
245 }
246
247 /**
248 * Cross product of two 3D vectors.
249 * @param v1 The first vector, an array of three floats.
250 * @param v2 The first vector, an array of three floats.
251 * @param resultOut Output containing the cross product.
252 *
253 */
254 void
255 MathFunctions::crossProduct(
256 const float v1[],
257 const float v2[],
258 float resultOut[])
259 {
260 resultOut[0] = v1[1] * v2[2] - v1[2] * v2[1];
261 resultOut[1] = v1[2] * v2[0] - v1[0] * v2[2];
262 resultOut[2] = v1[0] * v2[1] - v1[1] * v2[0];
263 }
264
265 /**
266 * Cross product of two 3D vectors.
267 * @param v1 The first vector, an array of three floats.
268 * @param v2 The first vector, an array of three floats.
269 * @param resultOut Output containing the cross product.
270 *
271 */
272 void
273 MathFunctions::crossProduct(
274 const double v1[],
275 const double v2[],
276 double resultOut[])
277 {
278 resultOut[0] = v1[1] * v2[2] - v1[2] * v2[1];
279 resultOut[1] = v1[2] * v2[0] - v1[0] * v2[2];
280 resultOut[2] = v1[0] * v2[1] - v1[1] * v2[0];
281 }
282
283 /**
284 * Cross product of two 3D vectors with normalizing both the
285 * input and output vectors.
286 *
287 * @param x1 The first vector, an array of three floats.
288 * @param x2 The first vector, an array of three floats.
289 * @return The cross product, an array of three floats.
290 *
291 */
292 void
293 MathFunctions::normalizedCrossProduct(
294 const float x1[],
295 const float x2[],
296 float resultOut[])
297 {
298 float v1[3] = { x1[0], x1[1], x1[2] };
299 MathFunctions::normalizeVector(v1);
300 float v2[3] = { x2[0], x2[1], x2[2] };
301 MathFunctions::normalizeVector(v2);
302
303 MathFunctions::crossProduct(v1, v2, resultOut);
304 MathFunctions::normalizeVector(resultOut);
305 }
306
307 /**
308 * Normalize a 3D vector (make its length 1.0).
309 *
310 * @param vectorsAll Array containing the XYZ components
311 * of the vectors.
312 * @param offset Offset of the vector's X-component in the
313 * vectorsAll array.
314 * @return The length of the vector prior to normalization.
315 *
316 */
317 float
318 MathFunctions::normalizeVector(
319 float vectorsAll[],
320 const int32_t offset)
321 {
322 float len = MathFunctions::vectorLength(vectorsAll, offset);
323 if (len != 0.0) {
324 vectorsAll[offset] /= len;
325 vectorsAll[offset+1] /= len;
326 vectorsAll[offset+2] /= len;
327 }
328 return len;
329 }
330
331 /**
332 * Normalize a 3D vector (make its length 1.0).
333 *
334 * @param vectorInOut vector that is normalized.
335 * @return The length of the vector prior to normalization.
336 *
337 */
338 float
339 MathFunctions::normalizeVector(float vector[3])
340 {
341 float len = vectorLength(vector);
342 if (len != 0.0) {
343 vector[0] /= len;
344 vector[1] /= len;
345 vector[2] /= len;
346 }
347 return len;
348 }
349
350 /**
351 * Normalize a 3D vector (make its length 1.0).
352 *
353 * @param vectorInOut vector that is normalized.
354 * @return The length of the vector prior to normalization.
355 *
356 */
357 double
358 MathFunctions::normalizeVector(double vector[3])
359 {
360 double len = vectorLength(vector);
361 if (len != 0.0) {
362 vector[0] /= len;
363 vector[1] /= len;
364 vector[2] /= len;
365 }
366 return len;
367 }
368
369 /**
370 * Get length of vector.
371 *
372 * @param vector Vector whose length is computed.
373 *
374 * @return The length of the vector.
375 *
376 */
377 float
378 MathFunctions::vectorLength(const float vector[3])
379 {
380 float len =
381 (float)std::sqrt(vector[0]*vector[0] +
382 vector[1]*vector[1] +
383 vector[2]*vector[2]);
384 return len;
385 }
386
387 /**
388 * Get length of vector.
389 *
390 * @param vectorsAll Array containing three-dimensional vectors.
391 * @param offset Offset of vector's X-component in vectorsAll array.
392 *
393 * @return The length of the vector.
394 *
395 */
396 float
397 MathFunctions::vectorLength(
398 const float vectorsAll[],
399 const int32_t offset)
400 {
401 float len =
402 (float)std::sqrt(vectorsAll[offset]*vectorsAll[offset] +
403 vectorsAll[offset+1]*vectorsAll[offset+1] +
404 vectorsAll[offset+2]*vectorsAll[offset+2]);
405 return len;
406 }
407
408 /**
409 * Get length of vector.
410 *
411 * @param vector Vector whose length is computed.
412 *
413 * @return The length of the vector.
414 *
415 */
416 double
417 MathFunctions::vectorLength(const double vector[3])
418 {
419 double len = std::sqrt(vector[0]*vector[0] +
420 vector[1]*vector[1] +
421 vector[2]*vector[2]);
422 return len;
423 }
424
425 /**
426 * Get the squared distance between two 3D points.
427 *
428 * @param p1 Point 1 (3 element array)
429 * @param p2 Point 2 (3 element array)
430 * @return Distance squared between the two points.
431 *
432 */
433 float
434 MathFunctions::distanceSquared3D(
435 const float p1[3],
436 const float p2[3])
437 {
438 float dx = p1[0] - p2[0];
439 float dy = p1[1] - p2[1];
440 float dz = p1[2] - p2[2];
441 float distSQ = dx*dx + dy*dy + dz*dz;
442 return distSQ;
443 }
444
445 /**
446 * Get the squared distance between two 3D coordinates.
447 *
448 * @param xyzAll Array containing all of the XYZ coordinates.
449 * @param offsetCoord1 Offset of the first coordinates X-coordinate.
450 * @param offsetCoord2 Offset of the second coordinates X-coordinate.
451 * @return Distance squared between the two coordinates.
452 *
453 */
454 float
455 MathFunctions::distanceSquared3D(
456 const float xyzAll[],
457 const int32_t offsetCoord1,
458 const int32_t offsetCoord2)
459 {
460 float dx = xyzAll[offsetCoord1] - xyzAll[offsetCoord2];
461 float dy = xyzAll[offsetCoord1+1] - xyzAll[offsetCoord2+1];
462 float dz = xyzAll[offsetCoord1+2] - xyzAll[offsetCoord2+2];
463 float distSQ = dx*dx + dy*dy + dz*dz;
464 return distSQ;
465 }
466
467 /**
468 * Get the distance between two 3D points.
469 *
470 * @param p1 Point 1 (3 element array)
471 * @param p2 Point 2 (3 element array)
472 * @return Distance between the two points.
473 *
474 */
475 float
476 MathFunctions::distance3D(
477 const float p1[3],
478 const float p2[3])
479 {
480 float dist = distanceSquared3D(p1, p2);
481 if (dist != 0.0f) {
482 dist = (float)std::sqrt(dist);
483 }
484 return dist;
485 }
486
487 /**
488 * Get the squared distance between two 3D points.
489 *
490 * @param p1 Point 1 (3 element array)
491 * @param p2 Point 2 (3 element array)
492 * @return Distance squared between the two points.
493 *
494 */
495 double
496 MathFunctions::distanceSquared3D(
497 const double p1[3],
498 const double p2[3])
499 {
500 double dx = p1[0] - p2[0];
501 double dy = p1[1] - p2[1];
502 double dz = p1[2] - p2[2];
503 double distSQ = dx*dx + dy*dy + dz*dz;
504 return distSQ;
505 // double dist = distanceSquared3D(p1, p2);
506 // if (dist != 0.0f) {
507 // dist = std::sqrt(dist);
508 // }
509 // return dist;
510 }
511
512 /**
513 * Get the distance between two 3D points.
514 *
515 * @param p1 Point 1 (3 element array)
516 * @param p2 Point 2 (3 element array)
517 * @return Distance between the two points.
518 *
519 */
520 double
521 MathFunctions::distance3D(
522 const double p1[3],
523 const double p2[3])
524 {
525 double dist = distanceSquared3D(p1, p2);
526 if (dist != 0.0f) {
527 dist = std::sqrt(dist);
528 }
529 return dist;
530 }
531
532 /**
533 * subtract vectors (3d) result = v1 - v2.
534 * @param v1 1st vector input
535 * @param v2 2nd vector input
536 * @param resultOut output, 3D vector containing result of subtraction.
537 *
538 */
539 void
540 MathFunctions::subtractVectors(
541 const float v1[3],
542 const float v2[3],
543 float resultOut[3])
544 {
545 resultOut[0] = v1[0] - v2[0];
546 resultOut[1] = v1[1] - v2[1];
547 resultOut[2] = v1[2] - v2[2];
548 }
549
550 void MathFunctions::addVectors(const float v1[3], const float v2[3], float resultOut[3])
551 {
552 resultOut[0] = v1[0] + v2[0];
553 resultOut[1] = v1[1] + v2[1];
554 resultOut[2] = v1[2] + v2[2];
555 }
556
557 /**
558 * Create the unit vector for a vector that starts at startXYZ and
559 * ends at endXYZ.
560 *
561 * @param startXYZ - Starting position of vector.
562 * @param endXYZ - Ending position of vector.
563 * @param unitVectorOut - output, vector starting at startXYZ and pointing to endXYZ.
564 *
565 */
566 void
567 MathFunctions::createUnitVector(
568 const float startXYZ[3],
569 const float endXYZ[3],
570 float unitVectorOut[3])
571 {
572 unitVectorOut[0] = endXYZ[0] - startXYZ[0];
573 unitVectorOut[1] = endXYZ[1] - startXYZ[1];
574 unitVectorOut[2] = endXYZ[2] - startXYZ[2];
575
576 MathFunctions::normalizeVector(unitVectorOut);
577 }
578
579 /**
580 * Create the unit vector for a vector that starts at startXYZ and
581 * ends at endXYZ.
582 *
583 * @param startXYZ - Starting position of vector.
584 * @param endXYZ - Ending position of vector.
585 * @param unitVectorOut - output, vector starting at startXYZ and pointing to endXYZ.
586 *
587 */
588 void
589 MathFunctions::createUnitVector(
590 const double startXYZ[3],
591 const double endXYZ[3],
592 double unitVectorOut[3])
593 {
594 unitVectorOut[0] = endXYZ[0] - startXYZ[0];
595 unitVectorOut[1] = endXYZ[1] - startXYZ[1];
596 unitVectorOut[2] = endXYZ[2] - startXYZ[2];
597
598 MathFunctions::normalizeVector(unitVectorOut);
599 }
600
601 /**
602 * Dot produce of three dimensional vectors.
603 * @param p1 vector 1
604 * @param p2 vector 2
605 * @return Dot product of the two vectors.
606 *
607 */
608 float
609 MathFunctions::dotProduct(
610 const float p1[3],
611 const float p2[3])
612 {
613 float dot = p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2];
614 return dot;
615 }
616
617 /**
618 * Dot produce of three dimensional vectors.
619 * @param p1 vector 1
620 * @param p2 vector 2
621 * @return Dot product of the two vectors.
622 *
623 */
624 double
625 MathFunctions::dotProduct(
626 const double p1[3],
627 const double p2[3])
628 {
629 return p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2];
630 }
631
632 /**
633 * Calculate the area for a triangle.
634 * @param v1 - XYZ coordinates for vertex 1
635 * @param v2 - XYZ coordinates for vertex 2
636 * @param v3 - XYZ coordinates for vertex 3
637 *
638 * @return Area of triangle.
639 *
640 */
641 float
642 MathFunctions::triangleArea(
643 const float v1[3],
644 const float v2[3],
645 const float v3[3])
646 {
647 /*
648 * Using doubles for the intermediate calculations
649 * produces results different from that if floats
650 * were used in the "area" equation. I'm
651 * assuming double is more accurate (JWH).
652 */
653 double a = MathFunctions::distanceSquared3D(v1,v2);
654 double b = MathFunctions::distanceSquared3D(v2,v3);
655 double c = MathFunctions::distanceSquared3D(v3,v1);
656 float area =
657 (float)(0.25f* std::sqrt(std::abs(4.0*a*c - (a-b+c)*(a-b+c))));
658 return area;
659 }
660
661 /**
662 * Calculate the area for a triangle (with doubles)
663 * @param v1 - XYZ coordinates for vertex 1
664 * @param v2 - XYZ coordinates for vertex 2
665 * @param v3 - XYZ coordinates for vertex 3
666 *
667 * @return Area of triangle.
668 *
669 */
670 float
671 MathFunctions::triangleArea(const double v1[3],
672 const double v2[3],
673 const double v3[3])
674 {
675 /*
676 * Using doubles for the intermediate calculations
677 * produces results different from that if floats
678 * were used in the "area" equation. I'm
679 * assuming double is more accurate (JWH).
680 */
681 double a = MathFunctions::distanceSquared3D(v1,v2);
682 double b = MathFunctions::distanceSquared3D(v2,v3);
683 double c = MathFunctions::distanceSquared3D(v3,v1);
684 float area =
685 (float)(0.25f* std::sqrt(std::abs(4.0*a*c - (a-b+c)*(a-b+c))));
686 return area;
687 }
688
689 /**
690 * Calculate the area of a triangle formed by 3 coordinates.
691 * @param xyzAll One-dimensional array containing the XYZ coordinates.
692 * @param offsetCoord1 Offset of node 1's X-coordinate which is
693 * followed by the Y- and Z-coordinates.
694 * @param offsetCoord2 Offset of node 2's X-coordinate which is
695 * followed by the Y- and Z-coordinates.
696 * @param offsetCoord3 Offset of node 3's X-coordinate which is
697 * followed by the Y- and Z-coordinates.
698 * @return Area of the triangle formed by the coordinates.
699 *
700 */
701 float
702 MathFunctions::triangleArea(
703 const float xyzAll[],
704 const int32_t offsetCoord1,
705 const int32_t offsetCoord2,
706 const int32_t offsetCoord3)
707 {
708 /*
709 * Using doubles for the intermediate calculations
710 * produces results different from that if floats
711 * were used in the "area" equation. I'm
712 * assuming double is more accurate (JWH).
713 */
714 double a = MathFunctions::distanceSquared3D(xyzAll, offsetCoord1, offsetCoord2);
715 double b = MathFunctions::distanceSquared3D(xyzAll, offsetCoord2, offsetCoord3);
716 double c = MathFunctions::distanceSquared3D(xyzAll, offsetCoord3, offsetCoord1);
717 float area =
718 (float)(0.25f* std::sqrt(std::abs(4.0*a*c - (a-b+c)*(a-b+c))));
719 return area;
720 }
721
722 /**
723 * Compute the signed area of a triangle in 2D.
724 * @param p1 - 1st coordinate of triangle
725 * @param p2 - 2nd coordinate of triangle
726 * @param p3 - 3rd coordinate of triangle
727 * @return Signed area of triangle which is positive if the vertices
728 * are in counter-clockwise orientation or negative if the vertices
729 * are in clockwise orientation.
730 *
731 */
732 float
733 MathFunctions::triangleAreaSigned2D(
734 const float p1[2],
735 const float p2[2],
736 const float p3[2])
737 {
738 float area = ( p1[0]*p2[1] + p2[0]*p3[1] + p3[0]*p1[1]
739 - p1[1]*p2[0] - p2[1]*p3[0] - p3[1]*p1[0] ) * 0.5f;
740 return area;
741 }
742
743 /**
744 * Compute the signed area of a triangle in 3D.
745 * @param referenceNormal - Normal vector.
746 * @param p1 - 1st coordinate of triangle
747 * @param p2 - 2nd coordinate of triangle
748 * @param p3 - 3rd coordinate of triangle
749 * @return Signed area of triangle which is positive if the vertices
750 * are in counter-clockwise orientation or negative if the vertices
751 * are in clockwise orientation.
752 *
753 */
754 float
755 MathFunctions::triangleAreaSigned3D(
756 const float referenceNormal[3],
757 const float p1[3],
758 const float p2[3],
759 const float p3[3])
760 {
761 //
762 // Area of the triangle formed by the three points
763 //
764 float area = triangleArea(p1, p2, p3);
765
766 //
767 // Normal for the three points
768 //
769 float triangleNormal[3];
770 MathFunctions::normalVector(p1, p2, p3, triangleNormal);
771
772 //
773 // Dot Product is the cosine of the angle between the two normals. When this value is less
774 // than zero, the absolute angle between the normals is greater than 90 degrees.
775 //
776 float dot = MathFunctions::dotProduct(referenceNormal, triangleNormal);
777 if (dot < 0.0) {
778 area = -area;
779 }
780
781 return area;
782 }
783
784 /**
785 * Determine if 2D line segments intersect.
786 * Algorithm from http://mathworld.wolfram.com/Line-LineIntersection.html
787 *
788 * @param p1 Line 1 end point 1.
789 * @param p2 Line 1 end point 2.
790 * @param q1 Line 2 end point 1.
791 * @param q2 Line 2 end point 2.
792 * @param tolerance Tolerance around the vertices (essentially
793 * lengthens lines by this quantity). Caret5 set this
794 * parameter to 0.01.
795 * @param intersectionOut Location of intersection.
796 * @return true if the line segments intersect else false.
797 *
798 */
799 bool
800 MathFunctions::lineIntersection2D(
801 const float p1[2],
802 const float p2[2],
803 const float q1[2],
804 const float q2[2],
805 const float tolerance,
806 float intersectionOut[2])
807 {
808 double tol = tolerance;
809 double x1 = p1[0];
810 double y1 = p1[1];
811 double x2 = p2[0];
812 double y2 = p2[1];
813
814 double x3 = q1[0];
815 double y3 = q1[1];
816 double x4 = q2[0];
817 double y4 = q2[1];
818
819 double denom = ((x1 - x2) * (y3 - y4)) - ((x3 - x4) * (y1 - y2));
820
821 if (denom != 0.0) {
822 double a = (x1 * y2) - (x2 * y1);
823 double c = (x3 * y4) - (x4 * y3);
824 double x = ((a * (x3 - x4)) - (c * (x1 - x2))) / denom;
825 double y = ((a * (y3 - y4)) - (c * (y1 - y2))) / denom;
826
827 double pxMax = std::max(x1, x2) + tol;
828 double pxMin = std::min(x1, x2) - tol;
829 double pyMax = std::max(y1, y2) + tol;
830 double pyMin = std::min(y1, y2) - tol;
831
832 double qxMax = std::max(x3, x4) + tol;
833 double qxMin = std::min(x3, x4) - tol;
834 double qyMax = std::max(y3, y4) + tol;
835 double qyMin = std::min(y3, y4) - tol;
836
837 intersectionOut[0] = (float)x;
838 intersectionOut[1] = (float)y;
839 if ((x >= pxMin) && (x <= pxMax) && (x >= qxMin) && (x <= qxMax) &&
840 (y >= pyMin) && (y <= pyMax) && (y >= qyMin) && (y <= qyMax)) {
841 return true;
842 }
843 }
844
845 return false;
846 }
847
848 /**
849 * Determine if a ray intersects a plane.
850 * @param p1 - 1st point defining the plane
851 * @param p2 - 2nd point defining the plane
852 * @param p3 - 3rd point defining the plane
853 * @param rayOrigin - origin of the ray
854 * @param rayVector - vector defining the ray
855 * @param intersectionXYZandDistance - An array of four that will contain
856 * the XYZ or the intersection point and the distance from the plane.
857 * @return true if the ray intersects the plane, else false.
858 *
859 */
860 bool
861 MathFunctions::rayIntersectPlane(
862 const float p1[3],
863 const float p2[3],
864 const float p3[3],
865 const float rayOrigin[3],
866 const float rayVector[3],
867 float intersectionXYZandDistance[4])
868 {
869 // Convert the ray into a unit vector
870 //
871 double ray[3] = { rayVector[0], rayVector[1], rayVector[2] };
872 MathFunctions::normalizeVector(ray);
873
874 //
875 // Normal of plane
876 //
877 float normal[3];
878 MathFunctions::normalVector(p1, p2, p3, normal);
879
880 //
881 // Compute the plane equation
882 //
883 double A = normal[0];
884 double B = normal[1];
885 double C = normal[2];
886 double D = -(A*p1[0] + B*p1[1] + C*p1[2]);
887
888 //
889 // Parametric coordinate of where ray intersects plane
890 //
891 double denom = A * ray[0] + B * ray[1] + C * ray[2];
892 if (denom != 0) {
893 const double t = -(A * rayOrigin[0] + B * rayOrigin[1] + C * rayOrigin[2] + D) / denom;
894
895 intersectionXYZandDistance[0] = (float)(rayOrigin[0] + ray[0] * t);
896 intersectionXYZandDistance[1] = (float)(rayOrigin[1] + ray[1] * t);
897 intersectionXYZandDistance[2] = (float)(rayOrigin[2] + ray[2] * t);
898
899 intersectionXYZandDistance[3] = (float)t;
900
901 return true;
902 }
903
904 return false;
905 }
906
907 /**
908 * Project a point to a plane.
909 * @param pt - the point to project.
910 * @param origin - point in the plane.
911 * @param normal - normal vector of plane.
912 * @param projectedPointOut - output, the projected position of "pt" on the plane.
913 *
914 */
915 void
916 MathFunctions::projectPoint(
917 const float pt[3],
918 const float origin[3],
919 const float normal[3],
920 float projectedPointOut[3])
921 {
922 float xo[3] = {
923 pt[0] - origin[0],
924 pt[1] - origin[1],
925 pt[2] - origin[2]
926 };
927
928 float t = MathFunctions::dotProduct(normal, xo);
929
930 projectedPointOut[0] = pt[0] - t * normal[0];
931 projectedPointOut[1] = pt[1] - t * normal[1];
932 projectedPointOut[2] = pt[2] - t * normal[2];
933 }
934
935 /**
936 * Get the signed distance from the plane to the "queryPoint".
937 * A positive distance indicates "queryPoint" is above the plane
938 * and a negative distance indicates "queryPoint" is below
939 * the plane.
940 *
941 * @param planeNormal - plane's normal vector.
942 * @param pointInPlane - point on the plane.
943 * @param queryPoint - the query point for which distance to plane is sought.
944 *
945 * @return Distance from the plane to the query point.
946 *
947 */
948 float
949 MathFunctions::signedDistanceFromPlane(
950 const float planeNormal[3],
951 const float pointInPlane[3],
952 const float queryPoint[3])
953 {
954 // Find out where query point projects on the plane
955 //
956 float queryPointProjectedOntoPlane[3];
957 MathFunctions::projectPoint(queryPoint, pointInPlane, planeNormal, queryPointProjectedOntoPlane);
958 float dx = planeNormal[0]
959 * (queryPoint[0] - queryPointProjectedOntoPlane[0]);
960 float dy = planeNormal[1]
961 * (queryPoint[1] - queryPointProjectedOntoPlane[1]);
962 float dz = planeNormal[2]
963 * (queryPoint[2] - queryPointProjectedOntoPlane[2]);
964 float dist = dx + dy + dz;
965
966 return dist;
967 }
968
969 /**
970 * Limit the "value" to be inclusively between the minimum and maximum.
971 * @param value - Value for testing.
972 * @param minimumValue - Minimum inclusive value.
973 * @param maximumValue - Maximum inclusive value.
974 * @return Value limited inclusively to the minimum and maximum values.
975 *
976 */
977 int32_t
978 MathFunctions::limitRange(
979 const int32_t value,
980 const int32_t minimumValue,
981 const int32_t maximumValue)
982 {
983 if (value < minimumValue) {
984 return minimumValue;
985 }
986
987 if (value > maximumValue) {
988 return maximumValue;
989 }
990
991 return value;
992 }
993
994 /**
995 * Limit the "value" to be inclusively between the minimum and maximum.
996 * @param value - Value for testing.
997 * @param minimumValue - Minimum inclusive value.
998 * @param maximumValue - Maximum inclusive value.
999 * @return Value limited inclusively to the minimum and maximum values.
1000 *
1001 */
1002 float
1003 MathFunctions::limitRange(
1004 const float value,
1005 const float minimumValue,
1006 const float maximumValue)
1007 {
1008 if (value < minimumValue) {
1009 return minimumValue;
1010 }
1011
1012 if (value > maximumValue) {
1013 return maximumValue;
1014 }
1015
1016 return value;
1017 }
1018
1019 /**
1020 * Limit the "value" to be inclusively between the minimum and maximum.
1021 * @param value - Value for testing.
1022 * @param minimumValue - Minimum inclusive value.
1023 * @param maximumValue - Maximum inclusive value.
1024 * @return Value limited inclusively to the minimum and maximum values.
1025 *
1026 */
1027 double
1028 MathFunctions::limitRange(
1029 const double value,
1030 const double minimumValue,
1031 const double maximumValue)
1032 {
1033 if (value < minimumValue) {
1034 return minimumValue;
1035 }
1036
1037 if (value > maximumValue) {
1038 return maximumValue;
1039 }
1040
1041 return value;
1042 }
1043
1044 /**
1045 * Find the distance from the point to the line defined by p1 and p2.
1046 * Formula is from
1047 * "http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html".
1048 *
1049 * @param p1 - First coordinate in line.
1050 * @param p2 - Second coordinate in line.
1051 * @param point - coordinate for which distance to line is sought.
1052 * @return Distance from point to the line (p1, p2).
1053 *
1054 */
1055 float
1056 MathFunctions::distanceToLine3D(
1057 const float p1[3],
1058 const float p2[3],
1059 const float point[3])
1060 {
1061 float dv2v1[3];
1062 MathFunctions::subtractVectors(p2, p1, dv2v1);
1063 float dv1pt[3];
1064 MathFunctions::subtractVectors(p1, point, dv1pt);
1065
1066 float crossed[3];
1067 MathFunctions::crossProduct(dv2v1, dv1pt, crossed);
1068
1069 float numerator = MathFunctions::vectorLength(crossed);
1070 float denomenator = MathFunctions::vectorLength(dv2v1);
1071
1072 float dist = numerator / denomenator;
1073
1074 return dist;
1075 }
1076
1077 /**
1078 * Determine if two arrays are equal, same number of elements and
1079 * corresponding elements equal.
1080 *
1081 * @param a - first array.
1082 * @param b - second array.
1083 * @return true if arrays are equal, else false.
1084 *
1085 */
1086 bool
1087 MathFunctions::arraysEqual(
1088 const float a[],
1089 const float b[],
1090 const int numElements)
1091 {
1092 for (int i = 0; i < numElements; i++) {
1093 if (a[i] != b[i]) {
1094 return false;
1095 }
1096 }
1097
1098 return true;
1099 }
1100
1101 /**
1102 * Get the average of three coordinates.
1103 * @param c1 - coordinate 1
1104 * @param c2 - coordinate 2
1105 * @param c3 - coordinate 3
1106 * @param outputAverage A three-dimensional array into
1107 * which the average of the three coordinates is
1108 * placed.
1109 *
1110 */
1111 void
1112 MathFunctions::averageOfThreeCoordinates(
1113 const float c1[3],
1114 const float c2[3],
1115 const float c3[3],
1116 float outputAverage[3])
1117 {
1118 outputAverage[0] = (c1[0] + c2[0] + c3[0]) / 3.0f;
1119 outputAverage[1] = (c1[1] + c2[1] + c3[1]) / 3.0f;
1120 outputAverage[2] = (c1[2] + c2[2] + c3[2]) / 3.0f;
1121 }
1122
1123 /**
1124 * Calculate the average of 3 coordinates.
1125 * @param xyzAll One-dimensional array containing the XYZ coordinates.
1126 * @param offsetCoord1 Offset of node 1's X-coordinate which is
1127 * followed by the Y- and Z-coordinates.
1128 * @param offsetCoord2 Offset of node 2's X-coordinate which is
1129 * followed by the Y- and Z-coordinates.
1130 * @param offsetCoord3 Offset of node 3's X-coordinate which is
1131 * followed by the Y- and Z-coordinates.
1132 * @param outputAverage 3 dimensional array passed in, into which
1133 * the average is placed.
1134 * @param outputOffset Offset of average into outputAverage array.
1135 *
1136 */
1137 void
1138 MathFunctions::averageOfThreeCoordinates(
1139 const float xyzAll[],
1140 const int32_t offsetCoord1,
1141 const int32_t offsetCoord2,
1142 const int32_t offsetCoord3,
1143 float outputAverage[],
1144 const int32_t outputOffset)
1145 {
1146 outputAverage[outputOffset] = (xyzAll[offsetCoord1] + xyzAll[offsetCoord2] + xyzAll[offsetCoord3]) / 3.0f;
1147 outputAverage[outputOffset+1] = (xyzAll[offsetCoord1+1] + xyzAll[offsetCoord2+1] + xyzAll[offsetCoord3+1]) / 3.0f;
1148 outputAverage[outputOffset+2] = (xyzAll[offsetCoord1+2] + xyzAll[offsetCoord2+2] + xyzAll[offsetCoord3+2]) / 3.0f;
1149 }
1150
1151 /**
1152 * Angle formed by p1, p2, p3 (angle at p2). Returned angle is in radians.
1153 * This method uses Java Math.acos() and produces highly accurate results.
1154 * @param p1 - point.
1155 * @param p2 - point.
1156 * @param p3 - point.
1157 * @return Angle formed by points.
1158 *
1159 */
1160 float
1161 MathFunctions::angle(
1162 const float p1[3],
1163 const float p2[3],
1164 const float p3[3])
1165 {
1166 //
1167 // Vector from P2 to P1
1168 //
1169 float v21[3] = { p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2] };
1170
1171 //
1172 // Vector from P2 to P3
1173 //
1174 float v23[3] = { p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2] };
1175
1176 //
1177 // Normalize the vectors
1178 //
1179 float v21len = MathFunctions::normalizeVector(v21);
1180 float v23len = MathFunctions::normalizeVector(v23);
1181
1182 float angleOut = 0.0f;
1183 if ((v21len > 0.0) && (v23len > 0.0)) { //
1184 // angle is inverse cosine of the dot product
1185 // and be sure to handle numerical errors.
1186 //
1187 float dot = MathFunctions::dotProduct(v21, v23);
1188 if (dot > 1.0f) dot = 1.0f;
1189 else if (dot < -1.0f) dot = -1.0f;
1190 angleOut = (float)std::acos(dot);
1191 }
1192
1193 return angleOut;
1194 }
1195
1196 /**
1197 * Signed angle for "jik".
1198 * @param pi - point.
1199 * @param pj - point.
1200 * @param pk - point.
1201 * @param n - normal
1202 * @return signed angle formed by the points.
1203 *
1204 */
1205 float
1206 MathFunctions::signedAngle(
1207 const float pi[3],
1208 const float pj[3],
1209 const float pk[3],
1210 const float n[3])
1211 {
1212 float x1 = pj[0] - pi[0];
1213 float y1 = pj[1] - pi[1];
1214 float z1 = pj[2] - pi[2];
1215 float x2 = pk[0] - pi[0];
1216 float y2 = pk[1] - pi[1];
1217 float z2 = pk[2] - pi[2];
1218
1219 /* s = |(ji)||(ki)| sin(phi) by cross product */
1220 float dx = y1*z2 - y2*z1;
1221 float dy = x2*z1 - x1*z2;
1222 float dz = x1*y2 - x2*y1;
1223 float t = (dx*n[0]) + (dy*n[1]) + (dz*n[2]);
1224 float s = (float)std::sqrt((dx*dx) + (dy*dy) + (dz*dz));
1225 if (t < 0.0f) {
1226 s = -s;
1227 }
1228
1229 /* c = |(ji)||(ki)| cos(phi) by inner product */
1230 float c = x1*x2 + y1*y2 + z1*z2;
1231 float phi = (float)std::atan2(s,c);
1232 return phi;
1233 }
1234
1235 /**
1236 * Determine if an integer is an odd number.
1237 * @param number Integer to test.
1238 * @return true if integer is odd, else false.
1239 *
1240 */
1241 bool
1242 MathFunctions::isOddNumber(const int32_t number)
1243 {
1244 bool result = ((number & 1) != 0);
1245 return result;
1246 }
1247
1248 /**
1249 * Determine if an integer is an odd number.
1250 * @param number Integer to test.
1251 * @return true if integer is odd, else false.
1252 *
1253 */
1254 bool
1255 MathFunctions::isEvenNumber(const int32_t number)
1256 {
1257 bool result = ((number & 1) == 0);
1258 return result;
1259 }
1260
1261 /**
1262 * Determine if two arrays are equal.
1263 * @param a1 First array.
1264 * @param a2 Second array.
1265 * @param tolerance Allowable difference in elements at same index.
1266 * @return true if arrays are of same length and corresponding
1267 * elements have a difference less than tolerance.
1268 *
1269 */
1270 bool
1271 MathFunctions::compareArrays(
1272 const float a1[],
1273 const float a2[],
1274 const int numElements,
1275 const float tolerance)
1276 {
1277 for (int i = 0; i < numElements; i++) {
1278 float diff = a1[i] - a2[i];
1279 if (diff < 0.0f) diff = -diff;
1280 if (diff > tolerance) {
1281 return false;
1282 }
1283 }
1284
1285 return true;
1286 }
1287
1288 /**
1289 * Clamp a value to the range minimum to maximum.
1290 * @param value Value for clamping.
1291 * @param minimum Minimum allowed value.
1292 * @param maximum Maximum allowed value.
1293 * @return Value clamped to minimum and maximum.
1294 *
1295 */
1296 int32_t
1297 MathFunctions::clamp(
1298 const int32_t value,
1299 const int32_t minimum,
1300 const int32_t maximum)
1301 {
1302 return MathFunctions::limitRange(value, minimum, maximum);
1303 }
1304
1305 /**
1306 * Clamp a value to the range minimum to maximum.
1307 * @param value Value for clamping.
1308 * @param minimum Minimum allowed value.
1309 * @param maximum Maximum allowed value.
1310 * @return Value clamped to minimum and maximum.
1311 *
1312 */
1313 float
1314 MathFunctions::clamp(
1315 const float value,
1316 const float minimum,
1317 const float maximum)
1318 {
1319 return MathFunctions::limitRange(value, minimum, maximum);
1320 }
1321
1322 /**
1323 * Distance SQUARED from (x1, y1) to (x2, y2)
1324 * @param X-coordinate of first point.
1325 * @param Y-coordinate of first point.
1326 * @param X-coordinate of second point.
1327 * @param Y-coordinate of second point.
1328 * @return Distance squared between the points.
1329 */
1330 double
1331 MathFunctions::distanceSquared2D(const double x1,
1332 const double y1,
1333 const double x2,
1334 const double y2)
1335 {
1336 const double dx = x2 - x1;
1337 const double dy = y2 - y1;
1338 const double d = (dx*dx) + (dy*dy);
1339 return d;
1340 }
1341
1342 uint32_t MathFunctions::gcd(uint32_t num1, uint32_t num2)
1343 {
1344 if (num1 == 0 || num2 == 0)
1345 {//catch zeros
1346 return 0;//gcd(0,x)=gcd(x,0)=0, seems less confusing than returning x
1347 }
1348 //modulus method for good worst-case asymptotic performance
1349 uint32_t temp;
1350 if (num2 > num1)//num1 kept as the larger number to simplify the code
1351 {
1352 temp = num1;
1353 num1 = num2;
1354 num2 = temp;
1355 }
1356 while (num2)
1357 {//maintain num2 as the smaller number
1358 temp = num1 % num2;//modulus to reduce the larger as much as possible, result will be smaller than num2
1359 num1 = num2;//so, we need to swap them
1360 num2 = temp;//when result becomes zero, num1 is our gcd
1361 }
1362 return num1;
1363 }
1364
1365 bool MathFunctions::isInf(const float number)
1366 {
1367 return (std::fabs(number) > 1.0f && number * 2.0f == number);
1368 }
1369
1370 bool MathFunctions::isNaN(const float number)
1371 {
1372 return (number != number);
1373 }
1374
1375 bool MathFunctions::isNegInf(const float number)
1376 {
1377 return (number < -1.0f && number * 2.0f == number);
1378 }
1379
1380 bool MathFunctions::isNumeric(const float number)
1381 {
1382 return (!isNaN(number) && !isInf(number));
1383 }
1384
1385 bool MathFunctions::isPosInf(const float number)
1386 {
1387 return (number > 1.0f && number * 2.0f == number);
1388 }
1389
1390 void MathFunctions::quaternToMatrix(const float cijk[4], float matrix[3][3])
1391 {//formula from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion
1392 double qlengthsqr = cijk[0] * cijk[0] + cijk[1] * cijk[1] + cijk[2] * cijk[2] + cijk[3] * cijk[3];
1393 double mult = 0.0;
1394 if (qlengthsqr > 0.0f)
1395 {
1396 mult = 2.0f / qlengthsqr;
1397 }
1398 double ijkmult[4] = { cijk[1] * mult, cijk[2] * mult, cijk[3] * mult };
1399 double wX = cijk[0] * ijkmult[0], wY = cijk[0] * ijkmult[1], wZ = cijk[0] * ijkmult[2];
1400 double xX = cijk[1] * ijkmult[0], xY = cijk[1] * ijkmult[1], xZ = cijk[1] * ijkmult[2];
1401 double yY = cijk[2] * ijkmult[1], yZ = cijk[2] * ijkmult[2];
1402 double zZ = cijk[3] * ijkmult[2];
1403 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
1404 matrix[0][1] = xY - wZ;
1405 matrix[0][2] = xZ + wY;
1406 matrix[1][0] = xY + wZ;
1407 matrix[1][1] = 1.0 - (xX + zZ);
1408 matrix[1][2] = yZ - wX;
1409 matrix[2][0] = xZ - wY;
1410 matrix[2][1] = yZ + wX;
1411 matrix[2][2] = 1.0 - (xX + yY);
1412 }
1413
1414 void MathFunctions::quaternToMatrix(const double cijk[4], double matrix[3][3])
1415 {//formula from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion
1416 double qlengthsqr = cijk[0] * cijk[0] + cijk[1] * cijk[1] + cijk[2] * cijk[2] + cijk[3] * cijk[3];
1417 double mult = 0.0;
1418 if (qlengthsqr > 0.0f)
1419 {
1420 mult = 2.0f / qlengthsqr;
1421 }
1422 double ijkmult[4] = { cijk[1] * mult, cijk[2] * mult, cijk[3] * mult };
1423 double wX = cijk[0] * ijkmult[0], wY = cijk[0] * ijkmult[1], wZ = cijk[0] * ijkmult[2];
1424 double xX = cijk[1] * ijkmult[0], xY = cijk[1] * ijkmult[1], xZ = cijk[1] * ijkmult[2];
1425 double yY = cijk[2] * ijkmult[1], yZ = cijk[2] * ijkmult[2];
1426 double zZ = cijk[3] * ijkmult[2];
1427 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
1428 matrix[0][1] = xY - wZ;
1429 matrix[0][2] = xZ + wY;
1430 matrix[1][0] = xY + wZ;
1431 matrix[1][1] = 1.0 - (xX + zZ);
1432 matrix[1][2] = yZ - wX;
1433 matrix[2][0] = xZ - wY;
1434 matrix[2][1] = yZ + wX;
1435 matrix[2][2] = 1.0 - (xX + yY);
1436 }
1437
1438 bool MathFunctions::matrixToQuatern(const float matrix[3][3], float cijk[4])
1439 {//formulas from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion
1440 const float toler = 0.0001f;
1441 float ivec[3] = { matrix[0][0], matrix[1][0], matrix[2][0] };
1442 float jvec[3] = { matrix[0][1], matrix[1][1], matrix[2][1] };
1443 float kvec[3] = { matrix[0][2], matrix[1][2], matrix[2][2] };
1444 if (!(std::abs(1.0f - normalizeVector(ivec)) <= toler)) return false;//use the "not less than or equal to" trick to catch NaNs
1445 if (!(std::abs(1.0f - normalizeVector(jvec)) <= toler)) return false;
1446 if (!(std::abs(1.0f - normalizeVector(kvec)) <= toler)) return false;
1447 if (!(dotProduct(ivec, jvec) <= toler)) return false;
1448 if (!(dotProduct(ivec, kvec) <= toler)) return false;
1449 if (!(dotProduct(jvec, kvec) <= toler)) return false;
1450 float tempvec[3];
1451 crossProduct(ivec, jvec, tempvec);
1452 if (!(dotProduct(tempvec, kvec) >= 0.9f)) return false;//i cross j must be k, otherwise it contains a flip
1453 int method = 0;
1454 double trace = matrix[0][0] + matrix[1][1] + matrix[2][2];
1455 if (trace < 0.0)
1456 {
1457 method = 1;
1458 float tempf = matrix[0][0];
1459 if (matrix[1][1] > tempf)
1460 {
1461 method = 2;
1462 tempf = matrix[1][1];
1463 }
1464 if (matrix[2][2] > tempf)
1465 {
1466 method = 3;
1467 }
1468 }
1469 switch (method)
1470 {
1471 case 0:
1472 {
1473 double r = std::sqrt(1.0 + trace);
1474 double s = 0.5 / r;
1475 cijk[0] = 0.5 * r;
1476 cijk[1] = (matrix[2][1] - matrix[1][2]) * s;
1477 cijk[2] = (matrix[0][2] - matrix[2][0]) * s;
1478 cijk[3] = (matrix[1][0] - matrix[0][1]) * s;
1479 }
1480 break;
1481 case 1:
1482 {
1483 double r = std::sqrt(1.0 + matrix[0][0] - matrix[1][1] - matrix[2][2]);
1484 double s = 0.5 / r;
1485 cijk[0] = (matrix[2][1] - matrix[1][2]) * s;
1486 cijk[1] = 0.5 * r;
1487 cijk[2] = (matrix[0][1] + matrix[1][0]) * s;
1488 cijk[3] = (matrix[2][0] + matrix[0][2]) * s;
1489 }
1490 break;
1491 case 2:
1492 {//DISCLAIMER: these last two were worked out by pattern since they aren't on wikipedia
1493 double r = std::sqrt(1.0 - matrix[0][0] + matrix[1][1] - matrix[2][2]);
1494 double s = 0.5 / r;
1495 cijk[0] = (matrix[0][2] - matrix[2][0]) * s;
1496 cijk[1] = (matrix[0][1] + matrix[1][0]) * s;
1497 cijk[2] = 0.5 * r;
1498 cijk[3] = (matrix[1][2] + matrix[2][1]) * s;
1499 }
1500 break;
1501 case 3:
1502 {
1503 double r = std::sqrt(1.0 - matrix[0][0] - matrix[1][1] + matrix[2][2]);
1504 double s = 0.5 / r;
1505 cijk[0] = (matrix[1][0] - matrix[0][1]) * s;
1506 cijk[1] = (matrix[2][0] + matrix[0][2]) * s;
1507 cijk[2] = (matrix[1][2] + matrix[2][1]) * s;
1508 cijk[3] = 0.5 * r;
1509 }
1510 break;
1511 default:
1512 return false;
1513 }
1514 if (cijk[0] < 0.0f)
1515 {
1516 cijk[0] = -cijk[0];
1517 cijk[1] = -cijk[1];
1518 cijk[2] = -cijk[2];
1519 cijk[3] = -cijk[3];
1520 }
1521 return true;
1522 }
1523
1524 bool MathFunctions::matrixToQuatern(const double matrix[3][3], double cijk[4])
1525 {//formulas from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion
1526 const float toler = 0.0001f;
1527 double ivec[3] = { matrix[0][0], matrix[1][0], matrix[2][0] };
1528 double jvec[3] = { matrix[0][1], matrix[1][1], matrix[2][1] };
1529 double kvec[3] = { matrix[0][2], matrix[1][2], matrix[2][2] };
1530 if (!(std::abs(1.0f - normalizeVector(ivec)) <= toler)) return false;//use the "not less than or equal to" trick to catch NaNs
1531 if (!(std::abs(1.0f - normalizeVector(jvec)) <= toler)) return false;
1532 if (!(std::abs(1.0f - normalizeVector(kvec)) <= toler)) return false;
1533 if (!(dotProduct(ivec, jvec) <= toler)) return false;
1534 if (!(dotProduct(ivec, kvec) <= toler)) return false;
1535 if (!(dotProduct(jvec, kvec) <= toler)) return false;
1536 double tempvec[3];
1537 crossProduct(ivec, jvec, tempvec);
1538 if (!(dotProduct(tempvec, kvec) >= 0.9f)) return false;//i cross j must be k, otherwise it contains a flip
1539 int method = 0;
1540 double trace = matrix[0][0] + matrix[1][1] + matrix[2][2];
1541 if (trace < 0.0)
1542 {
1543 method = 1;
1544 float tempf = matrix[0][0];
1545 if (matrix[1][1] > tempf)
1546 {
1547 method = 2;
1548 tempf = matrix[1][1];
1549 }
1550 if (matrix[2][2] > tempf)
1551 {
1552 method = 3;
1553 }
1554 }
1555 switch (method)
1556 {
1557 case 0:
1558 {
1559 double r = std::sqrt(1.0 + trace);
1560 double s = 0.5 / r;
1561 cijk[0] = 0.5 * r;
1562 cijk[1] = (matrix[2][1] - matrix[1][2]) * s;
1563 cijk[2] = (matrix[0][2] - matrix[2][0]) * s;
1564 cijk[3] = (matrix[1][0] - matrix[0][1]) * s;
1565 }
1566 break;
1567 case 1:
1568 {
1569 double r = std::sqrt(1.0 + matrix[0][0] - matrix[1][1] - matrix[2][2]);
1570 double s = 0.5 / r;
1571 cijk[0] = (matrix[2][1] - matrix[1][2]) * s;
1572 cijk[1] = 0.5 * r;
1573 cijk[2] = (matrix[0][1] + matrix[1][0]) * s;
1574 cijk[3] = (matrix[2][0] + matrix[0][2]) * s;
1575 }
1576 break;
1577 case 2:
1578 {//DISCLAIMER: these last two were worked out by pattern since they aren't on wikipedia
1579 double r = std::sqrt(1.0 - matrix[0][0] + matrix[1][1] - matrix[2][2]);
1580 double s = 0.5 / r;
1581 cijk[0] = (matrix[0][2] - matrix[2][0]) * s;
1582 cijk[1] = (matrix[0][1] + matrix[1][0]) * s;
1583 cijk[2] = 0.5 * r;
1584 cijk[3] = (matrix[1][2] + matrix[2][1]) * s;
1585 }
1586 break;
1587 case 3:
1588 {
1589 double r = std::sqrt(1.0 - matrix[0][0] - matrix[1][1] + matrix[2][2]);
1590 double s = 0.5 / r;
1591 cijk[0] = (matrix[1][0] - matrix[0][1]) * s;
1592 cijk[1] = (matrix[2][0] + matrix[0][2]) * s;
1593 cijk[2] = (matrix[1][2] + matrix[2][1]) * s;
1594 cijk[3] = 0.5 * r;
1595 }
1596 break;
1597 default:
1598 return false;
1599 }
1600 if (cijk[0] < 0.0f)
1601 {
1602 cijk[0] = -cijk[0];
1603 cijk[1] = -cijk[1];
1604 cijk[2] = -cijk[2];
1605 cijk[3] = -cijk[3];
1606 }
1607 return true;
1608 }
1609
1610 /**
1611 * Return the remainder from the resulting division using the given values.
1612 *
1613 * This method is written to match the result produced by the remainder()
1614 * function that is part of C99 but not supported on all platforms.
1615 *
1616 * Code may appear verbose but it avoid functions calls to fabs(), ceil(),
1617 * and floor().
1618 *
1619 * Note: X is the numerator.
1620 * Y is the denominator.
1621 *
1622 * The remainder() functions compute the value r such that r = x - n*y,
1623 * where n is the integer nearest the exact value of x/y.
1624 *
1625 * If there are two integers closest to x/y, n shall be the even one.
1626 *
1627 * @param numerator
1628 * The numerator.
1629 * @param denominator
1630 * The denominator.
1631 * @return
1632 * The remainder from numerator divided by denominator.
1633 */
1634 double
1635 MathFunctions::remainder(const double numerator,
1636 const double denominator)
1637 {
1638 if (denominator == 0.0) {
1639 return 0.0;
1640 }
1641
1642 const double quotient = numerator / denominator;
1643
1644 /*
1645 * Integer value greater than or equal to the quotient
1646 * and its difference with the quotient (ceiling)
1647 */
1648 const int64_t nearestIntegerOne = static_cast<int64_t>(quotient + 0.5);
1649 double diffOne = quotient - nearestIntegerOne;
1650 if (diffOne < 0.0) diffOne = -diffOne;
1651
1652 /*
1653 * Integer value less than or equal to the quotient
1654 * and its difference with the quotient (floor)
1655 */
1656 const int64_t nearestIntegerTwo = static_cast<int64_t>(quotient - 0.5);
1657 double diffTwo = quotient - nearestIntegerTwo;
1658 if (diffTwo < 0.0) diffTwo = -diffTwo;
1659
1660 /*
1661 * Helps determine if the two integer value are the same
1662 * distance from the quotient (value will be very close
1663 * to zero).
1664 */
1665 double diffOneTwo = diffOne - diffTwo;
1666 if (diffOneTwo < 0.0) diffOneTwo = -diffOneTwo;
1667
1668 int64_t nearestInteger = 0;
1669
1670 /*
1671 * If the two integer values are the same distance from zero
1672 */
1673 if (diffOneTwo < 0.000001) {
1674 /*
1675 * Use the integer that is even.
1676 * Note that if an integer is even, first bit is zero.
1677 */
1678 if ((nearestIntegerOne & 1) == 0) {
1679 nearestInteger = nearestIntegerOne;
1680 }
1681 else {
1682 nearestInteger = nearestIntegerTwo;
1683 }
1684 }
1685 else if (diffOne < diffTwo) {
1686 nearestInteger = nearestIntegerOne;
1687 }
1688 else {
1689 nearestInteger = nearestIntegerTwo;
1690 }
1691
1692 const double remainderValue = numerator - nearestInteger * denominator;
1693 return remainderValue;
1694 }
1695
1696 /**
1697 * Return the value rounded to the nearest integral (integer) value.
1698 *
1699 * @param value
1700 * Value that is rounded.
1701 * @return
1702 * Value rounded to nearest integral value.
1703 */
1704 double
1705 MathFunctions::round(const double value)
1706 {
1707 if (value < 0.0) {
1708 return std::ceil(value - 0.5f);
1709 }
1710 return std::floor(value + 0.5f);
1711 }
1712
0 #ifndef __MATHFUNCTIONS_H__
1 #define __MATHFUNCTIONS_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <stdint.h>
31
32 namespace cifti {
33
34 /**
35 * Various mathematical functions.
36 */
37 //NOTE: in CiftiLib, we only reference matrixToQuatern and quaternToMatrix, both used only in NiftiHeader when using volume files, not cifti files
38 class MathFunctions {
39 public:
40 static int64_t combinations(
41 const int64_t n,
42 const int64_t k);
43
44 static int64_t permutations(
45 const int64_t n,
46 const int64_t k);
47
48 static int64_t factorial(const int64_t n);
49
50 static bool normalVector(
51 const float v1[3],
52 const float v2[3],
53 const float v3[3],
54 float normalVectorOut[3]);
55
56 static bool normalVector(
57 const double v1[3],
58 const double v2[3],
59 const double v3[3],
60 double normalVectorOut[3]);
61
62 static void normalVectorDirection(
63 const float v1[3],
64 const float v2[3],
65 const float v3[3],
66 float directionOut[3]);
67
68 static void crossProduct(
69 const float v1[],
70 const float v2[],
71 float resultOut[]);
72
73 static void crossProduct(
74 const double v1[],
75 const double v2[],
76 double resultOut[]);
77
78 static void normalizedCrossProduct(
79 const float x1[],
80 const float x2[],
81 float resultOut[]);
82
83 static float normalizeVector(
84 float vectorsAll[],
85 const int32_t offset);
86
87 static float normalizeVector(float vectorInOut[3]);
88
89 static double normalizeVector(double vectorInOut[3]);
90
91 static float vectorLength(const float vector[3]);
92
93 static float vectorLength(
94 const float vectorsAll[],
95 const int32_t offset);
96
97 static double vectorLength(const double vector[3]);
98
99 static float distanceSquared3D(
100 const float p1[3],
101 const float p2[3]);
102
103 static float distanceSquared3D(
104 const float xyzAll[],
105 const int32_t offsetCoord1,
106 const int32_t offsetCoord2);
107
108 static float distance3D(
109 const float p1[3],
110 const float p2[3]);
111
112 static double distanceSquared3D(
113 const double p1[3],
114 const double p2[3]);
115
116 static double distance3D(
117 const double p1[3],
118 const double p2[3]);
119
120 static double distanceSquared2D(const double x1,
121 const double y1,
122 const double x2,
123 const double y2);
124
125 static void subtractVectors(
126 const float v1[3],
127 const float v2[3],
128 float resultOut[3]);
129
130 static void addVectors(
131 const float v1[3],
132 const float v2[3],
133 float resultOut[3]);
134
135 static void createUnitVector(
136 const float startXYZ[3],
137 const float endXYZ[3],
138 float unitVectorOut[3]);
139
140 static void createUnitVector(
141 const double startXYZ[3],
142 const double endXYZ[3],
143 double unitVectorOut[3]);
144
145 static float dotProduct(
146 const float p1[3],
147 const float p2[3]);
148
149 static double dotProduct(
150 const double p1[3],
151 const double p2[3]);
152
153 static float triangleArea(
154 const float v1[3],
155 const float v2[3],
156 const float v3[3]);
157
158 static float triangleArea(const double v1[3],
159 const double v2[3],
160 const double v3[3]);
161
162 static float triangleArea(
163 const float xyzAll[],
164 const int32_t offsetCoord1,
165 const int32_t offsetCoord2,
166 const int32_t offsetCoord3);
167
168 static float triangleAreaSigned2D(
169 const float p1[3],
170 const float p2[3],
171 const float p3[3]);
172
173 static float triangleAreaSigned3D(
174 const float referenceNormal[3],
175 const float p1[3],
176 const float p2[3],
177 const float p3[3]);
178
179 static bool lineIntersection2D(
180 const float p1[3],
181 const float p2[3],
182 const float q1[3],
183 const float q2[3],
184 const float tolerance,
185 float intersectionOut[3]);
186
187 static bool rayIntersectPlane(
188 const float p1[3],
189 const float p2[3],
190 const float p3[3],
191 const float rayOrigin[3],
192 const float rayVector[3],
193 float intersectionXYZandDistance[3]);
194
195 static void projectPoint(
196 const float pt[3],
197 const float origin[3],
198 const float normal[3],
199 float projectedPointOut[3]);
200
201 static float signedDistanceFromPlane(
202 const float planeNormal[3],
203 const float pointInPlane[3],
204 const float queryPoint[3]);
205
206 static int32_t limitRange(
207 const int32_t value,
208 const int32_t minimumValue,
209 const int32_t maximumValue);
210
211 static float limitRange(
212 const float value,
213 const float minimumValue,
214 const float maximumValue);
215
216 static double limitRange(
217 const double value,
218 const double minimumValue,
219 const double maximumValue);
220
221 static float distanceToLine3D(
222 const float p1[3],
223 const float p2[3],
224 const float point[3]);
225
226 static bool arraysEqual(
227 const float a[],
228 const float b[],
229 const int32_t numElements);
230
231 static void averageOfThreeCoordinates(
232 const float c1[3],
233 const float c2[3],
234 const float c3[3],
235 float outputAverage[3]);
236
237 static void averageOfThreeCoordinates(
238 const float xyzAll[],
239 const int32_t offsetCoord1,
240 const int32_t offsetCoord2,
241 const int32_t offsetCoord3,
242 float outputAverage[],
243 const int32_t outputOffset);
244
245 static float angle(
246 const float p1[3],
247 const float p2[3],
248 const float p3[3]);
249
250 static float signedAngle(
251 const float pi[3],
252 const float pj[3],
253 const float pk[3],
254 const float n[3]);
255
256 static bool isOddNumber(const int32_t number);
257
258 static bool isEvenNumber(const int32_t number);
259
260 static bool isNaN(const float number);
261
262 static bool isPosInf(const float number);
263
264 static bool isNegInf(const float number);
265
266 ///true if either inf or -inf
267 static bool isInf(const float number);
268
269 ///true only if not NaN, inf, or -inf
270 static bool isNumeric(const float number);
271
272 static bool compareArrays(
273 const float a1[],
274 const float a2[],
275 const int32_t numElements,
276 const float tolerance);
277
278 static int32_t clamp(
279 const int32_t value,
280 const int32_t minimum,
281 const int32_t maximum);
282
283 static float clamp(
284 const float value,
285 const float minimum,
286 const float maximum);
287
288 ///greatest common divisor
289 static uint32_t gcd(uint32_t num1, uint32_t num2);
290
291 ///convert quaternion to rotation matrix
292 static void quaternToMatrix(const float cijk[4], float matrix[3][3]);
293
294 ///convert quaternion to rotation matrix
295 static void quaternToMatrix(const double cijk[4], double matrix[3][3]);
296
297 ///try to convert 3x3 matrix to quaternion (return false if not a rotation matrix)
298 static bool matrixToQuatern(const float matrix[3][3], float cijk[4]);
299
300 ///try to convert 3x3 matrix to quaternion (return false if not a rotation matrix)
301 static bool matrixToQuatern(const double matrix[3][3], double cijk[4]);
302
303 static double remainder(const double numerator,
304 const double denominator);
305
306 static double round(const double value);
307
308 };
309
310 } // namespace
311
312 #endif // __MATHFUNCTIONS_H__
0 #ifndef __MATRIX_UTILITIES_H__
1 #define __MATRIX_UTILITIES_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <vector>
31 #include <cmath>
32 #include "stdint.h"
33
34 using namespace std;
35
36 //NOTE: this is not intended to be used outside of FloatMatrix.cxx, error condition is a 0x0 matrix result
37 //NOTE: if a matrix has a row shorter than the first row, expect problems. Calling checkDim will look for this, but it is not used internally, as FloatMatrix maintains this invariant
38
39 namespace cifti {
40
41 class MatrixFunctions
42 {
43 typedef int64_t msize_t;//NOTE: must be signed due to using -1 as a sentinel
44
45 public:
46 ///
47 /// matrix multiplication
48 ///
49 template <typename T1, typename T2, typename T3, typename A>
50 static void multiply(const vector<vector<T1> > &left, const vector<vector<T2> > &right, vector<vector<T3> > &result);
51
52 ///
53 /// scalar multiplication
54 ///
55 template <typename T1, typename T2, typename T3>
56 static void multiply(const vector<vector<T1> > &left, const T2 right, vector<vector<T3> > &result);
57
58 ///
59 /// reduced row echelon form
60 ///
61 template <typename T>
62 static void rref(vector<vector<T> > &inout);
63
64 ///
65 /// matrix inversion - wrapper to rref for now
66 ///
67 template <typename T>
68 static void inverse(const vector<vector<T> > &in, vector<vector<T> > &result);
69
70 ///
71 /// matrix addition - for simple code
72 ///
73 template <typename T1, typename T2, typename T3>
74 static void add(const vector<vector<T1> > &left, const vector<vector<T2> > &right, vector<vector<T3> > &result);
75
76 ///
77 /// scalar addition - for simple code
78 ///
79 template <typename T1, typename T2, typename T3>
80 static void add(const vector<vector<T1> > &left, const T2 right, vector<vector<T3> > &result);
81
82 ///
83 /// matrix subtraction - for simple code
84 ///
85 template <typename T1, typename T2, typename T3>
86 static void subtract(const vector<vector<T1> > &left, const vector<vector<T2> > &right, vector<vector<T3> > &result);
87
88 ///
89 /// transpose - for simple code
90 ///
91 template <typename T>
92 static void transpose(const vector<vector<T> > &in, vector<vector<T> > &result);
93
94 ///
95 /// debugging - verify matrix is rectangular and show its dimensions - returns true if rectangular
96 ///
97 template <typename T>
98 static bool checkDim(const vector<vector<T> > &in);
99
100 ///
101 /// allocate a matrix, don't initialize
102 ///
103 template <typename T>
104 static void resize(const msize_t rows, const msize_t columns, vector<vector<T> > &result, bool destructive = false);
105
106 ///
107 /// allocate a matrix of specified size
108 ///
109 template <typename T>
110 static void zeros(const msize_t rows, const msize_t columns, vector<vector<T> > &result);
111
112 ///
113 /// allocate a matrix of specified size
114 ///
115 template <typename T>
116 static void ones(const msize_t rows, const msize_t columns, vector<vector<T> > &result);
117
118 ///
119 /// make an identity matrix
120 ///
121 template <typename T>
122 static void identity(const msize_t size, vector<vector<T> > &result);
123
124 ///
125 /// horizontally concatenate matrices
126 ///
127 template <typename T1, typename T2, typename T3>
128 static void horizCat(const vector<vector<T1> > &left, const vector<vector<T2> > &right, vector<vector<T3> > &result);
129
130 ///
131 /// vertically concatenate matrices
132 ///
133 template <typename T1, typename T2, typename T3>
134 static void vertCat(const vector<vector<T1> > &top, const vector<vector<T2> > &bottom, vector<vector<T3> > &result);
135
136 ///
137 /// grab a piece of a matrix
138 ///
139 template <typename T>
140 static void getChunk(const msize_t firstrow, const msize_t lastrow, const msize_t firstcol, const msize_t lastcol, const vector<vector<T> > &in, vector<vector<T> > &result);
141
142 private:
143 ///
144 /// reduced row echelon form that is faster on larger matrices, is called by rref() if the matrix is big enough
145 ///
146 template <typename T>
147 static void rref_big(vector<vector<T> > &inout);
148 };
149
150 template <typename T1, typename T2, typename T3, typename A>
151 void MatrixFunctions::multiply(const vector<vector<T1> >& left, const vector<vector<T2> >& right, vector<vector<T3> >& result)
152 {//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
153 msize_t leftrows = (msize_t)left.size(), rightrows = (msize_t)right.size(), leftcols, rightcols;
154 vector<vector<T3> > tempstorage, *tresult = &result;//pointer because you can't change a reference
155 bool copyout = false;
156 if (&left == &result || &right == &result)
157 {
158 copyout = true;
159 tresult = &tempstorage;
160 }
161 if (leftrows && rightrows)
162 {
163 leftcols = (msize_t)left[0].size();
164 rightcols = (msize_t)right[0].size();
165 if (leftcols && rightcols && (rightrows == leftcols))
166 {
167 resize(leftrows, rightcols, (*tresult), true);//could use zeros(), but common index last lets us zero at the same time
168 msize_t i, j, k;
169 for (i = 0; i < leftrows; ++i)
170 {
171 for (j = 0; j < rightcols; ++j)
172 {
173 A accum = 0;
174 for (k = 0; k < leftcols; ++k)
175 {
176 accum += left[i][k] * right[k][j];
177 }
178 (*tresult)[i][j] = accum;
179 }
180 }
181 } else {
182 result.resize(0);
183 return;
184 }
185 } else {
186 result.resize(0);
187 return;
188 }
189 if (copyout)
190 {
191 result = tempstorage;
192 }
193 }
194
195 template <typename T1, typename T2, typename T3>
196 void MatrixFunctions::multiply(const vector<vector<T1> > &left, const T2 right, vector<vector<T3> > &result)
197 {
198 msize_t leftrows = (msize_t)left.size(), leftcols;
199 bool doresize = true;
200 if (&left == &result)
201 {
202 doresize = false;//don't resize if an input is an output
203 }
204 if (leftrows)
205 {
206 leftcols = (msize_t)left[0].size();
207 if (leftcols)
208 {
209 if (doresize) resize(leftrows, leftcols, result, true);
210 msize_t i, j;
211 for (i = 0; i < leftrows; ++i)
212 {
213 for (j = 0; j < leftcols; ++j)
214 {
215 result[i][j] = left[i][j] * right;
216 }
217 }
218 } else {
219 result.resize(0);
220 return;
221 }
222 } else {
223 result.resize(0);
224 return;
225 }
226 }
227
228 template<typename T>
229 void MatrixFunctions::rref_big(vector<vector<T> > &inout)
230 {
231 msize_t rows = (msize_t)inout.size(), cols;
232 if (rows > 0)
233 {
234 cols = (msize_t)inout[0].size();
235 if (cols > 0)
236 {
237 vector<msize_t> pivots(rows, -1), missingPivots;
238 msize_t i, j, k, myrow = 0;
239 msize_t pivotrow;
240 T tempval;
241 for (i = 0; i < cols; ++i)
242 {
243 if (myrow >= rows) break;//no pivots left
244 tempval = 0;
245 pivotrow = -1;
246 for (j = myrow; j < rows; ++j)
247 {//only search below for new pivot
248 if (abs(inout[j][i]) > tempval)
249 {
250 pivotrow = (msize_t)j;
251 tempval = abs(inout[j][i]);
252 }
253 }
254 if (pivotrow == -1)
255 {//naively expect linearly dependence to show as an exact zero
256 missingPivots.push_back(i);//record the missing pivot
257 continue;//move to the next column
258 }
259 inout[pivotrow].swap(inout[myrow]);//STL swap via pointers for constant time row swap
260 pivots[myrow] = i;//save the pivot location for back substitution
261 tempval = inout[myrow][i];
262 inout[myrow][i] = (T)1;
263 for (j = i + 1; j < cols; ++j)
264 {
265 inout[myrow][j] /= tempval;//divide row by pivot
266 }
267 for (j = myrow + 1; j < rows; ++j)
268 {//zero ONLY below pivot for now
269 tempval = inout[j][i];
270 inout[j][i] = (T)0;
271 for (k = i + 1; k < cols; ++k)
272 {
273 inout[j][k] -= tempval * inout[myrow][k];
274 }
275 }
276 ++myrow;//increment row on successful pivot
277 }
278 msize_t numMissing = (msize_t)missingPivots.size();
279 if (myrow > 1)//if there is only 1 pivot, there is no back substitution to do
280 {
281 msize_t lastPivotCol = pivots[myrow - 1];
282 for (i = myrow - 1; i > 0; --i)//loop through pivots, can't zero above the top pivot so exclude it
283 {
284 msize_t pivotCol = pivots[i];
285 for (j = i - 1; j >= 0; --j)//loop through rows above pivot
286 {
287 tempval = inout[j][pivotCol];
288 inout[j][pivotCol] = (T)0;//flat zero the entry above the pivot
289 for (k = numMissing - 1; k >= 0; --k)//back substitute within pivot range where pivots are missing
290 {
291 msize_t missingCol = missingPivots[k];
292 if (missingCol <= pivotCol) break;//equals will never trip, but whatever
293 inout[j][missingCol] -= tempval * inout[i][missingCol];
294 }
295 for (k = lastPivotCol + 1; k < cols; ++k)//loop through elements that are outside the pivot area
296 {
297 inout[j][k] -= tempval * inout[i][k];
298 }
299 }
300 }
301 }
302 } else {
303 inout.resize(0);
304 return;
305 }
306 } else {
307 inout.resize(0);
308 return;
309 }
310 }
311
312 template<typename T>
313 void MatrixFunctions::rref(vector<vector<T> > &inout)
314 {
315 msize_t rows = (msize_t)inout.size(), cols;
316 if (rows)
317 {
318 cols = (msize_t)inout[0].size();
319 if (cols)
320 {
321 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
322 {
323 rref_big(inout);
324 return;
325 }
326 msize_t i, j, k, myrow = 0;
327 msize_t pivotrow;
328 T tempval;
329 for (i = 0; i < cols; ++i)
330 {
331 if (myrow >= rows) break;//no pivots left
332 tempval = 0;
333 pivotrow = -1;
334 for (j = myrow; j < rows; ++j)
335 {//only search below for new pivot
336 if (abs(inout[j][i]) > tempval)
337 {
338 pivotrow = (msize_t)j;
339 tempval = abs(inout[j][i]);
340 }
341 }
342 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
343 {//naively expect linearly dependence to show as an exact zero
344 continue;//move to the next column
345 }
346 inout[pivotrow].swap(inout[myrow]);//STL swap via pointers for constant time row swap
347 tempval = inout[myrow][i];
348 inout[myrow][i] = 1;
349 for (j = i + 1; j < cols; ++j)
350 {
351 inout[myrow][j] /= tempval;//divide row by pivot
352 }
353 for (j = 0; j < myrow; ++j)
354 {//zero above pivot
355 tempval = inout[j][i];
356 inout[j][i] = 0;
357 for (k = i + 1; k < cols; ++k)
358 {
359 inout[j][k] -= tempval * inout[myrow][k];
360 }
361 }
362 for (j = myrow + 1; j < rows; ++j)
363 {//zero below pivot
364 tempval = inout[j][i];
365 inout[j][i] = 0;
366 for (k = i + 1; k < cols; ++k)
367 {
368 inout[j][k] -= tempval * inout[myrow][k];
369 }
370 }
371 ++myrow;//increment row on successful pivot
372 }
373 } else {
374 inout.resize(0);
375 return;
376 }
377 } else {
378 inout.resize(0);
379 return;
380 }
381 }
382
383 template<typename T>
384 void MatrixFunctions::inverse(const vector<vector<T> > &in, vector<vector<T> > &result)
385 {//rref implementation, there are faster (more complicated) ways - if it isn't invertible, it will hand back something strange
386 msize_t inrows = (msize_t)in.size(), incols;
387 if (inrows)
388 {
389 incols = (msize_t)in[0].size();
390 if (incols == inrows)
391 {
392 vector<vector<T> > inter, inter2;
393 identity(incols, inter2);
394 horizCat(in, inter2, inter);
395 rref(inter);
396 getChunk(0, inrows, incols, incols * 2, inter, result);//already using a local variable, doesn't need to check for reference duplicity
397 } else {
398 result.resize(0);
399 return;
400 }
401 } else {
402 result.resize(0);
403 return;
404 }
405 }
406
407 template <typename T1, typename T2, typename T3>
408 void MatrixFunctions::add(const vector<vector<T1> >& left, const vector<vector<T2> >& right, vector<vector<T3> >& result)
409 {
410 msize_t inrows = (msize_t)left.size(), incols;
411 bool doresize = true;
412 if (&left == &result || &right == &result)
413 {
414 doresize = false;//don't resize if an input is an output - this is ok for addition, don't need a copy
415 }
416 if (inrows)
417 {
418 incols = (msize_t)left[0].size();
419 if (inrows == (msize_t)right.size() && incols == (msize_t)right[0].size())
420 {
421 if (doresize) resize(inrows, incols, result, true);
422 for (msize_t i = 0; i < inrows; ++i)
423 {
424 for (msize_t j = 0; j < incols; ++j)
425 {
426 result[i][j] = left[i][j] + right[i][j];
427 }
428 }
429 } else {
430 result.resize(0);//use empty matrix for error condition
431 return;
432 }
433 } else {
434 result.resize(0);
435 return;
436 }
437 }
438
439 template <typename T1, typename T2, typename T3>
440 void MatrixFunctions::add(const vector<vector<T1> >& left, const T2 right, vector<vector<T3> >& result)
441 {
442 msize_t inrows = (msize_t)left.size(), incols;
443 bool doresize = true;
444 if (&left == &result)
445 {
446 doresize = false;//don't resize if an input is an output - this is ok for addition, don't need a copy
447 }
448 if (inrows)
449 {
450 incols = (msize_t)left[0].size();
451 if (doresize) resize(inrows, incols, result, true);
452 for (msize_t i = 0; i < inrows; ++i)
453 {
454 for (msize_t j = 0; j < incols; ++j)
455 {
456 result[i][j] = left[i][j] + right;
457 }
458 }
459 } else {
460 result.resize(0);
461 return;
462 }
463 }
464
465 template <typename T1, typename T2, typename T3>
466 void MatrixFunctions::subtract(const vector<vector<T1> >& left, const vector<vector<T2> >& right, vector<vector<T3> >& result)
467 {
468 msize_t inrows = (msize_t)left.size(), incols;
469 bool doresize = true;
470 if (&left == &result || &right == &result)
471 {
472 doresize = false;//don't resize if an input is an output
473 }
474 if (inrows)
475 {
476 incols = (msize_t)left[0].size();
477 if (inrows == (msize_t)right.size() && incols == (msize_t)right[0].size())
478 {
479 if (doresize) resize(inrows, incols, result, true);
480 for (msize_t i = 0; i < inrows; ++i)
481 {
482 for (msize_t j = 0; j < incols; ++j)
483 {
484 result[i][j] = left[i][j] - right[i][j];
485 }
486 }
487 } else {
488 result.resize(0);
489 return;
490 }
491 } else {
492 result.resize(0);
493 return;
494 }
495 }
496
497 template<typename T>
498 void MatrixFunctions::transpose(const vector<vector<T> > &in, vector<vector<T> > &result)
499 {
500 msize_t inrows = (msize_t)in.size(), incols;
501 vector<vector<T> > tempstorage, *tresult = &result;
502 bool copyout = false;
503 if (&in == &result)
504 {
505 copyout = true;
506 tresult = &tempstorage;
507 }
508 if (inrows)
509 {
510 incols = (msize_t)in[0].size();
511 resize(incols, inrows, (*tresult), true);
512 for (msize_t i = 0; i < inrows; ++i)
513 {
514 for (msize_t j = 0; j < incols; ++j)
515 {
516 (*tresult)[j][i] = in[i][j];
517 }
518 }
519 } else {
520 result.resize(0);
521 }
522 if (copyout)
523 {
524 result = tempstorage;
525 }
526 }
527
528 template<typename T>
529 bool MatrixFunctions::checkDim(const vector<vector<T> > &in)
530 {
531 bool ret = true;
532 msize_t rows = (msize_t)in.size(), columns;
533 if (rows)
534 {
535 columns = (msize_t)in[0].size();
536 for (msize_t i = 1; i < rows; ++i)
537 {
538 if (in[i].size() != columns)
539 {
540 ret = false;
541 }
542 }
543 }
544 return ret;
545 }
546
547 template<typename T>
548 void MatrixFunctions::resize(const msize_t rows, const msize_t columns, vector<vector<T> >& result, bool destructive)
549 {
550 if (destructive && result.size() && ((msize_t)result.capacity() < rows || (msize_t)result[0].capacity() < columns))
551 {//for large matrices, copying to preserve contents is slow
552 result.resize(0);//not intended to dealloc, just to set number of items to copy to zero
553 }//default is nondestructive resize, copies everything
554 result.resize(rows);
555 for (msize_t i = 0; i < (const msize_t)rows; ++i)
556 {//naive method, may end up copying everything twice if both row and col resizes require realloc
557 result[i].resize(columns);
558 }
559 }
560
561 template<typename T>
562 void MatrixFunctions::zeros(const msize_t rows, const msize_t columns, vector<vector<T> >& result)
563 {
564 resize(rows, columns, result, true);
565 for (msize_t i = 0; i < rows; ++i)
566 {
567 for (msize_t j = 0; j < columns; ++j)
568 {
569 result[i][j] = 0;//should cast to float or double fine
570 }
571 }
572 }
573
574 template<typename T>
575 void MatrixFunctions::ones(const msize_t rows, const msize_t columns, vector<vector<T> >& result)
576 {
577 resize(rows, columns, result, true);
578 for (msize_t i = 0; i < rows; ++i)
579 {
580 for (msize_t j = 0; j < columns; ++j)
581 {
582 result[i][j] = 1;//should cast to float or double fine
583 }
584 }
585 }
586
587 template<typename T>
588 void MatrixFunctions::identity(const msize_t size, vector<vector<T> >& result)
589 {
590 resize(size, size, result, true);
591 for (msize_t i = 0; i < (const msize_t)size; ++i)
592 {
593 for (msize_t j = 0; j < (const msize_t)size; ++j)
594 {
595 result[i][j] = ((i == j) ? 1 : 0);//ditto, forgive the ternary
596 }
597 }
598 }
599
600 template <typename T1, typename T2, typename T3>
601 void MatrixFunctions::horizCat(const vector<vector<T1> >& left, const vector<vector<T2> >& right, vector<vector<T3> >& result)
602 {
603 msize_t inrows = (msize_t)left.size(), leftcols, rightcols;
604 vector<vector<T3> > tempstorage, *tresult = &result;
605 bool copyout = false;
606 if (&left == &result || &right == &result)
607 {
608 copyout = true;
609 tresult = &tempstorage;
610 }
611 if (inrows && inrows == (msize_t)right.size())
612 {
613 leftcols = (msize_t)left[0].size();
614 rightcols = (msize_t)right[0].size();
615 (*tresult) = left;//use STL copy to start
616 resize(inrows, leftcols + rightcols, (*tresult));//values survive nondestructive resize
617 for (msize_t i = 0; i < inrows; ++i)
618 {
619 for (msize_t j = 0; j < rightcols; ++j)
620 {
621 (*tresult)[i][j + leftcols] = right[i][j];
622 }
623 }
624 } else {
625 result.resize(0);
626 return;
627 }
628 if (copyout)
629 {
630 result = tempstorage;
631 }
632 }
633
634 template <typename T1, typename T2, typename T3>
635 void MatrixFunctions::vertCat(const vector<vector<T1> >& top, const vector<vector<T2> >& bottom, vector<vector<T3> >& result)
636 {
637 msize_t toprows = (msize_t)top.size(), botrows = (msize_t)bottom.size(), incols;
638 vector<vector<T3> > tempstorage, *tresult = &result;
639 bool copyout = false;
640 if (&top == &result || &bottom == &result)
641 {
642 copyout = true;
643 tresult = &tempstorage;
644 }
645 if (toprows && botrows)
646 {
647 incols = (msize_t)top[0].size();
648 if (incols == (msize_t)bottom[0].size())
649 {
650 (*tresult) = top;
651 resize(toprows + botrows, incols, (*tresult));//nondestructive resize
652 for (msize_t i = 0; i < botrows; ++i)
653 {
654 for (msize_t j = 0; j < incols; ++j)
655 {
656 (*tresult)[i + toprows][j] = bottom[i][j];
657 }
658 }
659 } else {
660 result.resize(0);
661 return;
662 }
663 } else {
664 result.resize(0);
665 return;
666 }
667 if (copyout)
668 {
669 result = tempstorage;
670 }
671 }
672
673 template<typename T>
674 void MatrixFunctions::getChunk(const msize_t firstrow, const msize_t lastrow, const msize_t firstcol, const msize_t lastcol, const vector<vector<T> >& in, vector<vector<T> >& result)
675 {
676 msize_t outrows = lastrow - firstrow;
677 msize_t outcols = lastcol - firstcol;
678 if (lastrow <= firstrow || lastcol <= firstcol || firstrow < 0 || firstcol < 0 || lastrow > (msize_t)in.size() || lastcol > (msize_t)in[0].size())
679 {
680 result.resize(0);
681 return;
682 }
683 vector<vector<T> > tempstorage, *tresult = &result;
684 bool copyout = false;
685 if (&in == &result)
686 {
687 copyout = true;
688 tresult = &tempstorage;
689 }
690 resize(outrows, outcols, (*tresult), true);
691 for (msize_t i = 0; i < outrows; ++i)
692 {
693 for (msize_t j = 0; j < outcols; ++j)
694 {
695 (*tresult)[i][j] = in[i + firstrow][j + firstcol];
696 }
697 }
698 if (copyout)
699 {
700 result = tempstorage;
701 }
702 }
703
704 }
705
706 #endif
707
0 #ifndef __MULTI_DIM_ARRAY_H__
1 #define __MULTI_DIM_ARRAY_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "CiftiAssert.h"
31
32 #include "stdint.h"
33 #include <vector>
34
35 namespace cifti
36 {
37
38 template<typename T>
39 class MultiDimArray
40 {
41 std::vector<int64_t> m_dims, m_skip;//always use int64_t for indexes internally
42 std::vector<T> m_data;
43 template<typename I>
44 int64_t index(const int& fullDims, const std::vector<I>& indexSelect) const;//assume we never need over 2 billion dimensions
45 public:
46 const std::vector<int64_t>& getDimensions() const { return m_dims; }
47 template<typename I>
48 void resize(const std::vector<I>& dims);//destructive resize
49 template<typename I>
50 T& at(const std::vector<I>& pos);
51 template<typename I>
52 const T& at(const std::vector<I>& pos) const;
53 template<typename I>
54 T* get(const int& fullDims, const std::vector<I>& indexSelect);//subarray reference selection
55 template<typename I>
56 const T* get(const int& fullDims, const std::vector<I>& indexSelect) const;
57 };
58
59 template<typename T>
60 template<typename I>
61 void MultiDimArray<T>::resize(const std::vector<I>& dims)
62 {
63 m_dims = std::vector<int64_t>(dims.begin(), dims.end());
64 m_skip.resize(m_dims.size());
65 if (dims.size() == 0)
66 {
67 m_data.clear();
68 return;
69 }
70 int64_t numElems = 1;
71 for (int i = 0; i < (int)m_dims.size(); ++i)
72 {
73 CiftiAssert(m_dims[i] > 0);
74 m_skip[i] = numElems;
75 numElems *= m_dims[i];
76 }
77 m_data.resize(numElems);
78 }
79
80 template<typename T>
81 template<typename I>
82 int64_t MultiDimArray<T>::index(const int& fullDims, const std::vector<I>& indexSelect) const
83 {
84 CiftiAssert(fullDims + indexSelect.size() == m_dims.size());
85 int64_t ret = 0;
86 for (int i = fullDims; i < (int)m_dims.size(); ++i)
87 {
88 CiftiAssert(indexSelect[i - fullDims] >= 0 && indexSelect[i - fullDims] < m_dims[i]);
89 ret += m_skip[i] * indexSelect[i - fullDims];
90 }
91 return ret;
92 }
93
94 template<typename T>
95 template<typename I>
96 T& MultiDimArray<T>::at(const std::vector<I>& pos)
97 {
98 return m_data[index(0, pos)];
99 }
100
101 template<typename T>
102 template<typename I>
103 const T& MultiDimArray<T>::at(const std::vector<I>& pos) const
104 {
105 return m_data[index(0, pos)];
106 }
107
108 template<typename T>
109 template<typename I>
110 T* MultiDimArray<T>::get(const int& fullDims, const std::vector<I>& indexSelect)
111 {
112 return m_data.data() + index(fullDims, indexSelect);
113 }
114
115 template<typename T>
116 template<typename I>
117 const T* MultiDimArray<T>::get(const int& fullDims, const std::vector<I>& indexSelect) const
118 {
119 return m_data.data() + index(fullDims, indexSelect);
120 }
121 }
122
123 #endif //__MULTI_DIM_ARRAY_H__
0 #ifndef __MULTI_DIM_ITERATOR_H__
1 #define __MULTI_DIM_ITERATOR_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "stdint.h"
31 #include <vector>
32
33 namespace cifti
34 {
35
36 template<typename T>
37 class MultiDimIterator
38 {
39 std::vector<T> m_dims, m_pos;
40 bool m_atEnd;
41 void gotoBegin();
42 void gotoLast();
43 public:
44 explicit MultiDimIterator(const std::vector<T>& dimensions);
45 void operator++();
46 void operator++(int);
47 void operator--();
48 void operator--(int);
49 const std::vector<T>& operator*() const { return m_pos; }
50 bool atEnd() const { return m_atEnd; }
51 };
52
53 template<typename T>
54 MultiDimIterator<T>::MultiDimIterator(const std::vector<T>& dimensions)
55 {
56 m_dims = dimensions;
57 gotoBegin();
58 }
59
60 template<typename T>
61 void MultiDimIterator<T>::gotoBegin()
62 {
63 m_pos = std::vector<T>(m_dims.size(), 0);
64 m_atEnd = false;
65 size_t numDims = m_dims.size();
66 for (size_t i = 0; i < numDims; ++i)
67 {
68 if (m_dims[i] < 1)
69 {
70 m_atEnd = true;
71 break;
72 }
73 }
74 }
75
76 template<typename T>
77 void MultiDimIterator<T>::gotoLast()
78 {
79 m_pos = std::vector<T>(m_dims.size());
80 m_atEnd = false;
81 size_t numDims = m_dims.size();
82 for (size_t i = 0; i < numDims; ++i)
83 {
84 m_pos[i] = m_dims[i] - 1;
85 if (m_dims[i] < 1)
86 {
87 m_atEnd = true;
88 }
89 }
90 }
91
92 template<typename T>
93 void MultiDimIterator<T>::operator++()
94 {
95 if (atEnd())//wrap around
96 {
97 gotoBegin();
98 return;
99 }
100 if (m_dims.size() == 0)
101 {
102 m_atEnd = true;//special case: no dimensions works the same as 1 dimension of length 1
103 return;
104 }
105 size_t numDims = m_dims.size();
106 for (size_t i = 0; i < numDims; ++i)
107 {
108 ++m_pos[i];
109 if (m_pos[i] < m_dims[i]) return;
110 m_pos[i] = 0;
111 }
112 m_atEnd = true;//if we didn't return already, all of them wrapped, so we are at the end
113 }
114
115 template<typename T>
116 void MultiDimIterator<T>::operator++(int)
117 {
118 ++(*this);
119 }
120
121 template<typename T>
122 void MultiDimIterator<T>::operator--()
123 {
124 if (atEnd())//wrap around
125 {
126 gotoLast();
127 return;
128 }
129 if (m_dims.size() == 0)
130 {
131 m_atEnd = true;//special case: no dimensions works the same as 1 dimension of length 1
132 return;
133 }
134 size_t numDims = m_dims.size();
135 for (size_t i = 0; i < numDims; ++i)
136 {
137 if (m_pos[i] > 0)
138 {
139 --m_pos[i];
140 return;
141 } else {
142 m_pos[i] = m_dims[i] - 1;
143 }
144 }
145 m_atEnd = true;//if we didn't return already, all of them wrapped, so we are at the end
146 }
147
148 template<typename T>
149 void MultiDimIterator<T>::operator--(int)
150 {
151 --(*this);
152 }
153
154 }
155
156 #endif //__MULTI_DIM_ITERATOR_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "CiftiAssert.h"
28 #include "Vector3D.h"
29
30 #include <cmath>
31
32 using namespace std;
33 using namespace cifti;
34
35 Vector3D Vector3D::cross(const Vector3D& right) const
36 {
37 Vector3D ret;
38 ret[0] = m_vec[1] * right[2] - m_vec[2] * right[1];
39 ret[1] = m_vec[2] * right[0] - m_vec[0] * right[2];
40 ret[2] = m_vec[0] * right[1] - m_vec[1] * right[0];
41 return ret;
42 }
43
44 float Vector3D::dot(const Vector3D& right) const
45 {
46 return m_vec[0] * right[0] + m_vec[1] * right[1] + m_vec[2] * right[2];
47 }
48
49 float Vector3D::length() const
50 {
51 return sqrt(lengthsquared());
52 }
53
54 float Vector3D::lengthsquared() const
55 {
56 return m_vec[0] * m_vec[0] + m_vec[1] * m_vec[1] + m_vec[2] * m_vec[2];
57 }
58
59 Vector3D Vector3D::normal(float* origLength) const
60 {
61 Vector3D ret = *this;
62 float mylength = length();
63 if (mylength != 0.0f) ret /= mylength;
64 if (origLength != NULL)
65 {
66 *origLength = mylength;
67 }
68 return ret;
69 }
70
71 Vector3D::Vector3D()
72 {
73 m_vec[0] = 0.0f;
74 m_vec[1] = 0.0f;
75 m_vec[2] = 0.0f;
76 }
77
78 Vector3D::Vector3D(const float& x, const float& y, const float& z)
79 {
80 m_vec[0] = x;
81 m_vec[1] = y;
82 m_vec[2] = z;
83 }
84
85 Vector3D::Vector3D(const float* right)
86 {
87 m_vec[0] = right[0];
88 m_vec[1] = right[1];
89 m_vec[2] = right[2];
90 }
91
92 float& Vector3D::operator[](const int64_t& index)
93 {
94 CiftiAssert(index > -1 && index < 3);
95 return m_vec[index];
96 }
97
98 const float& Vector3D::operator[](const int64_t& index) const
99 {
100 CiftiAssert(index > -1 && index < 3);
101 return m_vec[index];
102 }
103
104 float& Vector3D::operator[](const int32_t& index)
105 {
106 CiftiAssert(index > -1 && index < 3);
107 return m_vec[index];
108 }
109
110 const float& Vector3D::operator[](const int32_t& index) const
111 {
112 CiftiAssert(index > -1 && index < 3);
113 return m_vec[index];
114 }
115
116 Vector3D Vector3D::operator*(const float& right) const
117 {
118 Vector3D ret = *this;
119 ret *= right;
120 return ret;
121 }
122
123 Vector3D& Vector3D::operator*=(const float& right)
124 {
125 m_vec[0] *= right;
126 m_vec[1] *= right;
127 m_vec[2] *= right;
128 return *this;
129 }
130
131 Vector3D cifti::operator*(const float& left, const Vector3D& right)
132 {
133 return right * left;
134 }
135
136 Vector3D Vector3D::operator+(const Vector3D& right) const
137 {
138 Vector3D ret = *this;
139 ret += right;
140 return ret;
141 }
142
143 Vector3D& Vector3D::operator+=(const Vector3D& right)
144 {
145 m_vec[0] += right.m_vec[0];
146 m_vec[1] += right.m_vec[1];
147 m_vec[2] += right.m_vec[2];
148 return *this;
149 }
150
151 Vector3D Vector3D::operator-(const Vector3D& right) const
152 {
153 Vector3D ret = *this;
154 ret -= right;
155 return ret;
156 }
157
158 Vector3D Vector3D::operator-() const
159 {
160 Vector3D ret;
161 ret.m_vec[0] = -m_vec[0];
162 ret.m_vec[1] = -m_vec[1];
163 ret.m_vec[2] = -m_vec[2];
164 return ret;
165 }
166
167 Vector3D& Vector3D::operator-=(const Vector3D& right)
168 {
169 m_vec[0] -= right.m_vec[0];
170 m_vec[1] -= right.m_vec[1];
171 m_vec[2] -= right.m_vec[2];
172 return *this;
173 }
174
175 Vector3D Vector3D::operator/(const float& right) const
176 {
177 Vector3D ret = *this;
178 ret /= right;
179 return ret;
180 }
181
182 Vector3D& Vector3D::operator/=(const float& right)
183 {
184 m_vec[0] /= right;
185 m_vec[1] /= right;
186 m_vec[2] /= right;
187 return *this;
188 }
189
190 Vector3D& Vector3D::operator=(const float* right)
191 {
192 m_vec[0] = right[0];
193 m_vec[1] = right[1];
194 m_vec[2] = right[2];
195 return *this;
196 }
0 #ifndef __VECTOR_3D_H__
1 #define __VECTOR_3D_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <cstddef>
31 #include "stdint.h"
32
33 namespace cifti {
34
35 class Vector3D
36 {
37 float m_vec[3];
38 public:
39 //vector functions
40 float dot(const Vector3D& right) const;
41 Vector3D cross(const Vector3D& right) const;
42 Vector3D normal(float* origLength = NULL) const;
43 float length() const;
44 float lengthsquared() const;
45 //constructors
46 Vector3D();
47 Vector3D(const float& x, const float& y, const float& z);
48 Vector3D(const float* right);
49 //compatibility operators
50 float& operator[](const int64_t& index);
51 const float& operator[](const int64_t& index) const;
52 float& operator[](const int32_t& index);
53 const float& operator[](const int32_t& index) const;
54 Vector3D& operator=(const float* right);
55 //numerical operators
56 Vector3D& operator+=(const Vector3D& right);
57 Vector3D& operator-=(const Vector3D& right);
58 Vector3D& operator*=(const float& right);
59 Vector3D& operator/=(const float& right);
60 Vector3D operator+(const Vector3D& right) const;
61 Vector3D operator-(const Vector3D& right) const;
62 Vector3D operator-() const;
63 Vector3D operator*(const float& right) const;
64 Vector3D operator/(const float& right) const;//NOTE: doesn't really make sense to have the other division, unlike multiplication
65 inline operator float*() { return m_vec; }
66 };
67
68 Vector3D operator*(const float& left, const Vector3D& right);
69
70 }
71 #endif //__VECTOR_3D_H__
0 #ifndef __VOXEL_IJK_H__
1 #define __VOXEL_IJK_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "stdint.h"
31
32 namespace cifti {
33
34 struct VoxelIJK
35 {
36 int64_t m_ijk[3];
37 VoxelIJK() { }
38 VoxelIJK(int64_t i, int64_t j, int64_t k) { m_ijk[0] = i; m_ijk[1] = j; m_ijk[2] = k; }
39 template<typename T>
40 VoxelIJK(const T ijk[3]) {
41 m_ijk[0] = ijk[0];
42 m_ijk[1] = ijk[1];
43 m_ijk[2] = ijk[2];
44 }
45 bool operator<(const VoxelIJK& rhs) const//so it kan be the key of a map
46 {
47 if (m_ijk[2] < rhs.m_ijk[2]) return true;//compare such that when sorted, m_ijk[0] moves fastest
48 if (m_ijk[2] > rhs.m_ijk[2]) return false;
49 if (m_ijk[1] < rhs.m_ijk[1]) return true;
50 if (m_ijk[1] > rhs.m_ijk[1]) return false;
51 return (m_ijk[0] < rhs.m_ijk[0]);
52 }
53 bool operator==(const VoxelIJK& rhs) const
54 {
55 return (m_ijk[0] == rhs.m_ijk[0] &&
56 m_ijk[1] == rhs.m_ijk[1] &&
57 m_ijk[2] == rhs.m_ijk[2]);
58 }
59 bool operator!=(const VoxelIJK& rhs) const { return !((*this) == rhs); }
60 };
61
62 }
63
64 #endif //__VOXEL_IJK_H__
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "XmlAdapter.h"
28
29 #include "CiftiAssert.h"
30
31 using namespace std;
32 using namespace cifti;
33
34 XmlAttributesResult cifti::XmlReader_parseAttributes(XmlReader& xml, const vector<AString>& mandatoryNames, const vector<AString>& optionalNames)
35 {
36 XmlAttributesResult ret;
37 int numMandatory = (int)mandatoryNames.size();//if you require more than 2 billion attributes, w3c would like a word with you
38 int numOptional = (int)optionalNames.size();
39 ret.mandatoryVals.resize(numMandatory);
40 ret.optionalVals.resize(numOptional);
41 AString elemName;
42 #ifdef CIFTILIB_USE_QT
43 vector<bool> mandatoryPresent(numMandatory, false);
44 map<AString, int> mandatoryMap, optionalMap;
45 for (int i = 0; i < numMandatory; ++i)//set up map lookups so it isn't n^2
46 {
47 mandatoryMap[mandatoryNames[i]] = i;
48 }
49 for (int i = 0; i < numOptional; ++i)
50 {
51 optionalMap[optionalNames[i]] = i;
52 }
53 CiftiAssert(xml.isStartElement());
54 elemName = xml.name().toString();
55 QXmlStreamAttributes myAttrs = xml.attributes();
56 int numAttrs = myAttrs.size();
57 for (int i = 0; i < numAttrs; ++i)
58 {
59 QString name = myAttrs[i].name().toString();
60 map<AString, int>::iterator iter = mandatoryMap.find(name);
61 if (iter == mandatoryMap.end())
62 {
63 iter = optionalMap.find(name);
64 if (iter != optionalMap.end())//NOTE: ignore unrecognized attributes for now, because MatrixIndicesMap has attributes that are used by different objects
65 {//other option is to include such attributes in both calls, even if not used there
66 ret.optionalVals[iter->second].present = true;
67 ret.optionalVals[iter->second].value = myAttrs[i].value().toString();
68 }
69 } else {
70 ret.mandatoryVals[iter->second] = myAttrs[i].value().toString();
71 mandatoryPresent[iter->second] = true;
72 }
73 }
74 for (int i = 0; i < numMandatory; ++i)
75 {
76 if (mandatoryPresent[i] == false)
77 {
78 throw CiftiException(elemName + " element is missing the " + mandatoryNames[i] + " attribute");
79 }
80 }
81 #else
82 #ifdef CIFTILIB_USE_XMLPP
83 CiftiAssert(xml.get_node_type() == XmlReader::Element);
84 elemName = xml.get_local_name();
85 for (int i = 0; i < numMandatory; ++i)//NOTE: libxml++ (and even libxml2 to some extent) don't have a good interface for iterating through all attributes efficiently
86 {//you have to iterate through a linked list of attributes exposed through a "hacking interface"
87 ret.mandatoryVals[i] = xml.get_attribute(mandatoryNames[i]);
88 if (ret.mandatoryVals[i] == "")//HACK: treat empty value same as missing attribute
89 {
90 throw CiftiException(elemName + " element is missing the " + mandatoryNames[i] + " attribute");
91 }
92 }
93 for (int i = 0; i < numOptional; ++i)
94 {
95 AString value = xml.get_attribute(optionalNames[i]);
96 if (value != "")//HACK: and again
97 {
98 ret.optionalVals[i].present = true;
99 ret.optionalVals[i].value = value;
100 }
101 }
102 #else
103 #error "not implemented"
104 #endif
105 #endif
106 return ret;
107 }
108
109 bool cifti::XmlReader_checkEndElement(XmlReader& xml, const AString& elementName)
110 {
111 #ifdef CIFTILIB_USE_QT
112 return xml.hasError() || (xml.isEndElement() && xml.name() == elementName);//if it has an xml error, don't trip an assert
113 #else
114 #ifdef CIFTILIB_USE_XMLPP
115 return (xml.get_node_type() == XmlReader::EndElement || xml.is_empty_element()) && xml.get_local_name() == elementName;
116 #else
117 #error "not implemented"
118 #endif
119 #endif
120 }
121
122 AString cifti::XmlReader_readElementText(XmlReader& xml)
123 {
124 #ifdef CIFTILIB_USE_QT
125 return xml.readElementText();//NOTE: requires calling code to check for xml.hasError() when using QT
126 #else
127 #ifdef CIFTILIB_USE_XMLPP
128 AString ret;
129 CiftiAssert(xml.get_node_type() == XmlReader::Element);
130 AString elemName = xml.get_local_name();
131 bool done = xml.is_empty_element();//NOTE: a <blah/> element does NOT give a separate end element state!!!
132 while(!done && xml.read())
133 {
134 switch(xml.get_node_type())
135 {
136 case XmlReader::Element:
137 throw CiftiException("unexpected element inside " + elemName + " element: " + xml.get_local_name());
138 case XmlReader::Text:
139 case XmlReader::CDATA:
140 ret += xml.get_value();
141 break;
142 case XmlReader::EndElement:
143 done = true;
144 break;
145 default:
146 break;
147 }
148 }
149 return ret;
150 #else
151 #error "not implemented"
152 #endif
153 #endif
154 }
155
156 bool cifti::XmlReader_readNextStartElement(XmlReader& xml)
157 {
158 #ifdef CIFTILIB_USE_QT
159 return xml.readNextStartElement();//NOTE: requires calling code to check for xml.hasError() when using QT
160 #else
161 #ifdef CIFTILIB_USE_XMLPP
162 if (xml.is_empty_element()) return false;//NOTE: a <blah/> element does NOT give a separate end element state!!!
163 while (xml.read())
164 {
165 switch (xml.get_node_type())
166 {
167 case XmlReader::Element:
168 return true;
169 case XmlReader::EndElement:
170 return false;
171 default:
172 break;
173 }
174 }
175 return false;
176 #else
177 #error "not implemented"
178 #endif
179 #endif
180 }
181
182 AString cifti::XmlReader_elementName(XmlReader& xml)
183 {
184 #ifdef CIFTILIB_USE_QT
185 return xml.name().toString();
186 #else
187 #ifdef CIFTILIB_USE_XMLPP
188 return xml.get_local_name();
189 #else
190 #error "not implemented"
191 #endif
192 #endif
193 }
0 #ifndef __XML_ADAPTER_H__
1 #define __XML_ADAPTER_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "AString.h"
31 #include "CiftiException.h"
32
33 #include <map>
34 #include <vector>
35
36 #ifdef __XML_ADAPTER_H_HAVE_IMPL__
37 #undef __XML_ADAPTER_H_HAVE_IMPL__
38 #endif
39
40 #ifdef CIFTILIB_USE_QT
41 #define __XML_ADAPTER_H_HAVE_IMPL__
42 #include <QXmlStreamReader>
43 #include <QXmlStreamWriter>
44 namespace cifti
45 {
46 typedef QXmlStreamReader XmlReader;
47 typedef QXmlStreamWriter XmlWriter;
48 }
49 #endif //CIFTILIB_USE_QT
50
51 #ifdef CIFTILIB_USE_XMLPP
52 #define __XML_ADAPTER_H_HAVE_IMPL__
53 #include "libxml++/libxml++.h"
54 #include "libxml++/parsers/textreader.h"
55 #include "libxml/xmlwriter.h"
56 #include "libxml/xmlstring.h"
57 namespace cifti
58 {
59 typedef xmlpp::TextReader XmlReader;
60 class XmlWriter
61 {//write our own wrapper for the C writing API, as libxml++ doesn't wrap it
62 xmlTextWriterPtr m_xmlPtr;
63 xmlBufferPtr m_bufPtr;
64 std::vector<AString> m_elementStack;//track element names for better error messages
65 public:
66 XmlWriter()
67 {//only support writing to memory
68 m_bufPtr = xmlBufferCreate();
69 if (m_bufPtr == NULL) throw CiftiException("error creating xml buffer");
70 m_xmlPtr = xmlNewTextWriterMemory(m_bufPtr, 0);
71 if (m_xmlPtr == NULL)
72 {
73 xmlBufferFree(m_bufPtr);
74 throw CiftiException("error creating xml writer");
75 }
76 if (xmlTextWriterSetIndent(m_xmlPtr, 1) != 0 || xmlTextWriterSetIndentString(m_xmlPtr, BAD_CAST " ") != 0)
77 {
78 throw CiftiException("error setting xml writer indentation");
79 }
80 }
81 ~XmlWriter()
82 {
83 xmlFreeTextWriter(m_xmlPtr);
84 xmlBufferFree(m_bufPtr);
85 }
86 void writeStartDocument()//copy a subset of the QXmlStreamWriter interface, so we don't have to rewrite much (any?) xml writing code
87 {
88 if (xmlTextWriterStartDocument(m_xmlPtr, NULL, NULL, NULL) == -1) throw CiftiException("error writing document start");
89 }
90 void writeEndDocument()
91 {
92 if (xmlTextWriterEndDocument(m_xmlPtr) == -1) throw CiftiException("error writing document end");
93 m_elementStack.clear();
94 }
95 void writeStartElement(const AString& name)
96 {
97 if (xmlTextWriterStartElement(m_xmlPtr, BAD_CAST ASTRING_UTF8_RAW(name)) == -1) throw CiftiException("error writing " + name + " element");
98 m_elementStack.push_back(name);
99 }
100 void writeEndElement()
101 {
102 if (m_elementStack.empty()) throw CiftiException("internal error: attempted writing end element outside root element");
103 if (xmlTextWriterEndElement(m_xmlPtr) == -1) throw CiftiException("error writing end element for " + m_elementStack.back());
104 m_elementStack.pop_back();
105 }
106 void writeCharacters(const AString& text)
107 {
108 if (xmlTextWriterWriteString(m_xmlPtr, BAD_CAST ASTRING_UTF8_RAW(text)) == -1) throw CiftiException("error writing element text");
109 }
110 void writeTextElement(const AString& name, const AString& text)
111 {
112 if (xmlTextWriterWriteElement(m_xmlPtr, BAD_CAST ASTRING_UTF8_RAW(name), BAD_CAST ASTRING_UTF8_RAW(text)) == -1)
113 {
114 throw CiftiException("error writing " + name + " element");
115 }
116 }
117 void writeAttribute(const AString& name, const AString& text)
118 {
119 if (m_elementStack.empty()) throw CiftiException("internal error: attempted writing attribute outside root element");
120 if (xmlTextWriterWriteAttribute(m_xmlPtr, BAD_CAST ASTRING_UTF8_RAW(name), BAD_CAST ASTRING_UTF8_RAW(text)) == -1)
121 {
122 throw CiftiException("error writing " + name + " attribute of " + m_elementStack.back() + " element");
123 }
124 }
125 std::vector<char> getXmlData() const
126 {
127 std::vector<char> ret(m_bufPtr->use);//this includes the null terminator?
128 for (unsigned int i = 0; i < m_bufPtr->use; ++i)
129 {
130 ret[i] = m_bufPtr->content[i];
131 }
132 return ret;
133 }
134 };
135
136 }
137 #endif //CIFTILIB_USE_XMLPP
138
139 #ifndef __XML_ADAPTER_H_HAVE_IMPL__
140 #error "you must define either CIFTILIB_USE_QT or CIFTILIB_USE_XMLPP to select what XML implementation to use"
141 #endif
142
143 namespace cifti
144 {
145 //helper functions that exist for all xml libraries
146 struct XmlAttributesResult
147 {
148 struct OptionalStatus
149 {
150 OptionalStatus() { present = false; }
151 bool present;
152 AString value;
153 };
154 std::vector<AString> mandatoryVals;
155 std::vector<OptionalStatus> optionalVals;
156 };
157
158 AString XmlReader_readElementText(XmlReader& xml);
159 bool XmlReader_readNextStartElement(XmlReader& xml);
160 AString XmlReader_elementName(XmlReader& xml);
161 XmlAttributesResult XmlReader_parseAttributes(XmlReader& xml, const std::vector<AString>& mandatoryNames, const std::vector<AString>& optionalNames = std::vector<AString>());
162 bool XmlReader_checkEndElement(XmlReader& xml, const AString& elementName);//for use in asserts at end of element parsing functions
163 }
164
165 #endif //__XML_ADAPTER_H__
0
1 project (Nifti)
2
3 SET(HEADERS
4 NiftiHeader.h
5 )
6
7 SET(SOURCES
8 NiftiHeader.cxx
9 )
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "NiftiHeader.h"
28
29 #include "Common/ByteSwapping.h"
30 #include "Common/CiftiAssert.h"
31 #include "Common/CiftiException.h"
32 #include "Common/FloatMatrix.h"
33 #include "Common/MathFunctions.h"
34
35 #include <cmath>
36 #include <cstring>
37 #include <iostream>
38 #include <limits>
39
40 using namespace std;
41 using namespace boost;
42 using namespace cifti;
43
44 NiftiHeader::NiftiHeader()
45 {
46 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
47 {
48 throw CiftiException("internal error: nifti header structs are the wrong size");//this is not a runtime assert, because we want this checked in release builds too
49 }
50 memset(&m_header, 0, sizeof(m_header));
51 for (int i = 0; i < 8; ++i)
52 {
53 m_header.dim[i] = 1;//we maintain 1s on unused dimensions to make some stupid nifti readers happy
54 m_header.pixdim[i] = 1.0f;//also set pixdims to 1 by default, to make some other nifti readers happy when reading cifti headers
55 }//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
56 m_header.xyzt_units = SPACE_TIME_TO_XYZT(NIFTI_UNITS_MM, NIFTI_UNITS_SEC);
57 m_header.scl_slope = 1.0;//default to identity scaling
58 m_header.scl_inter = 0.0;
59 m_header.datatype = NIFTI_TYPE_FLOAT32;
60 m_header.bitpix = typeToNumBits(m_header.datatype);
61 m_version = 0;
62 m_isSwapped = false;
63 }
64
65 bool NiftiHeader::canWriteVersion(const int& version) const
66 {
67 if (computeVoxOffset(version) < 0) return false;//error condition, can happen if an extension is longer than 2^31
68 if (version == 2) return true;//our internal state is nifti-2, return early
69 if (version != 1) return false;//we can only write 1 and 2
70 vector<int64_t> dims = getDimensions();
71 for (int i = 0; i < (int)dims.size(); ++i)
72 {
73 if (dims[i] > numeric_limits<int16_t>::max()) return false;
74 }
75 if (m_header.intent_code > numeric_limits<int16_t>::max() || m_header.intent_code < numeric_limits<int16_t>::min()) return false;
76 if (m_header.slice_code > numeric_limits<char>::max() || m_header.slice_code < numeric_limits<char>::min()) return false;
77 if (m_header.xyzt_units > numeric_limits<char>::max() || m_header.xyzt_units < numeric_limits<char>::min()) return false;
78 if (m_header.qform_code > numeric_limits<int16_t>::max() || m_header.qform_code < numeric_limits<int16_t>::min()) return false;
79 if (m_header.sform_code > numeric_limits<int16_t>::max() || m_header.sform_code < numeric_limits<int16_t>::min()) return false;
80 return true;
81 }
82
83 int64_t NiftiHeader::computeVoxOffset(const int& version) const
84 {
85 int64_t ret;
86 switch (version)
87 {
88 case 1:
89 ret = 4 + sizeof(nifti_1_header);//the 4 is the extender bytes
90 break;
91 case 2:
92 ret = 4 + sizeof(nifti_2_header);
93 break;
94 default:
95 return -1;
96 }
97 int numExtensions = (int)m_extensions.size();
98 for (int i = 0; i < numExtensions; ++i)
99 {
100 CiftiAssert(m_extensions[i] != NULL);
101 int64_t thisSize = 8 + m_extensions[i]->m_bytes.size();//8 is for the int32_t size and ecode for nifti-1 style extensions
102 if (thisSize % 16 != 0)//round up to nearest multiple of 16
103 {
104 int paddingBytes = 16 - (thisSize % 16);
105 thisSize += paddingBytes;
106 }
107 if (thisSize > numeric_limits<int32_t>::max()) return -1;//since we don't have nifti-2 style extensions yet, always fail
108 ret += thisSize;
109 }
110 if (version == 1)//need to put it into a float exactly (yes, really)
111 {
112 float temp = ret;
113 if (ret != (int64_t)temp) return -1;//for now, just fail, until it actually becomes a problem
114 }
115 return ret;
116 }
117
118 bool NiftiHeader::getDataScaling(double& mult, double& offset) const
119 {
120 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
121 {
122 mult = 1.0;//in case someone ignores the boolean
123 offset = 0.0;
124 return false;
125 }
126 mult = m_header.scl_slope;
127 offset = m_header.scl_inter;
128 return true;
129 }
130
131 vector<int64_t> NiftiHeader::getDimensions() const
132 {
133 CiftiAssert(m_header.dim[0] >= 0 && m_header.dim[0] <= 7);//because storage is private and initialized to zero, so it should never be invalid
134 vector<int64_t> ret(m_header.dim[0]);
135 for (int i = 0; i < m_header.dim[0]; ++i)
136 {
137 ret[i] = m_header.dim[i + 1];
138 }
139 return ret;
140 }
141
142 vector<std::vector<float> > NiftiHeader::getFSLSpace() const
143 {//don't look at me, blame analyze and flirt
144 vector<int64_t> dimensions = getDimensions();
145 if (dimensions.size() < 3) throw CiftiException("NiftiHeaderIO has less than 3 dimensions, can't generate the FSL space for it");
146 FloatMatrix ret;
147 vector<vector<float> > sform = getSForm();
148 float determinant = sform[0][0] * sform[1][1] * sform[2][2] +
149 sform[0][1] * sform[1][2] * sform[2][0] +
150 sform[0][2] * sform[1][0] * sform[2][1] -
151 sform[0][2] * sform[1][1] * sform[2][0] -
152 sform[0][0] * sform[1][2] * sform[2][1] -
153 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
154 ret = FloatMatrix::identity(4);//generate a 4x4 with 0 0 0 1 last row via FloatMatrix for convenience
155 if (determinant > 0.0f)
156 {
157 ret[0][0] = -m_header.pixdim[1];//yes, they really use pixdim, despite checking the SForm/QForm for flipping - ask them, not me
158 ret[0][3] = (dimensions[0] - 1) * m_header.pixdim[1];//note - pixdim[1] is for i, pixdim[0] is qfac
159 } else {
160 ret[0][0] = m_header.pixdim[1];
161 }
162 ret[1][1] = m_header.pixdim[2];
163 ret[2][2] = m_header.pixdim[3];
164 int32_t spaceUnit = XYZT_TO_SPACE(m_header.xyzt_units);
165 switch (spaceUnit)
166 {
167 case NIFTI_UNITS_METER:
168 ret *= 1000.0f;
169 ret[3][3] = 1.0f;
170 break;
171 case NIFTI_UNITS_MICRON:
172 ret *= 0.001f;
173 ret[3][3] = 1.0f;
174 break;
175 case NIFTI_UNITS_MM:
176 break;
177 default:
178 break;//will already have warned in getSForm()
179 }
180 return ret.getMatrix();
181 }
182
183 bool NiftiHeader::operator==(const NiftiHeader& rhs) const
184 {
185 if (m_version != rhs.m_version) return false;//this is to test for consistency, not to test if two headers mean the same thing
186 if (m_isSwapped != rhs.m_isSwapped) return false;
187 return memcmp(&m_header, &(rhs.m_header), sizeof(m_header)) == 0;
188 }
189
190 vector<vector<float> > NiftiHeader::getSForm() const
191 {
192 FloatMatrix ret = FloatMatrix::zeros(4, 4);
193 ret[3][3] = 1.0f;//force 0 0 0 1 last row
194 if (m_header.sform_code != 0)//prefer sform
195 {
196 for(int i = 0; i < 4; i++)
197 {
198 ret[0][i] = m_header.srow_x[i];
199 ret[1][i] = m_header.srow_y[i];
200 ret[2][i] = m_header.srow_z[i];
201 }
202 } else if (m_header.qform_code != 0) {//fall back to qform
203 float rotmat[3][3], quat[4];
204 quat[1] = m_header.quatern_b;
205 quat[2] = m_header.quatern_c;
206 quat[3] = m_header.quatern_d;
207 float checkquat = quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3];
208 if (checkquat <= 1.01f)//make sure qform is sane
209 {
210 if (checkquat > 1.0f)
211 {
212 quat[0] = 0.0f;
213 } else {
214 quat[0] = sqrt(1.0f - checkquat);
215 }
216 MathFunctions::quaternToMatrix(quat, rotmat);
217 for (int i = 0; i < 3; ++i)
218 {
219 for (int j = 0; j < 3; ++j)
220 {
221 rotmat[i][j] *= m_header.pixdim[i + 1];
222 }
223 }
224 if (m_header.pixdim[0] < 0.0f)//left handed coordinate system, flip the kvec
225 {
226 rotmat[0][2] = -rotmat[0][2];
227 rotmat[1][2] = -rotmat[1][2];
228 rotmat[2][2] = -rotmat[2][2];
229 }
230 for (int i = 0; i < 3; ++i)
231 {
232 for (int j = 0; j < 3; ++j)
233 {
234 ret[i][j] = rotmat[i][j];
235 }
236 }
237 ret[0][3] = m_header.qoffset_x;
238 ret[1][3] = m_header.qoffset_y;
239 ret[2][3] = m_header.qoffset_z;
240 } else {
241 cerr << "found quaternion with length greater than 1 in nifti header" << endl;
242 ret[0][0] = m_header.pixdim[1];
243 ret[1][1] = m_header.pixdim[2];
244 ret[2][2] = m_header.pixdim[3];
245 }
246 } else {//fall back to analyze and complain
247 cerr << "no sform or qform code found, using ANALYZE coordinates!" << endl;
248 ret[0][0] = m_header.pixdim[1];
249 ret[1][1] = m_header.pixdim[2];
250 ret[2][2] = m_header.pixdim[3];
251 }
252 int32_t spaceUnit = XYZT_TO_SPACE(m_header.xyzt_units);
253 switch (spaceUnit)
254 {
255 case NIFTI_UNITS_METER:
256 ret *= 1000.0f;
257 ret[3][3] = 1.0f;
258 break;
259 case NIFTI_UNITS_MICRON:
260 ret *= 0.001f;
261 ret[3][3] = 1.0f;
262 break;
263 case 0://assume millimeters if unspecified, could give a warning
264 case NIFTI_UNITS_MM:
265 break;
266 default:
267 cerr << "unrecognized spatial unit in nifti header" << endl;
268 }
269 return ret.getMatrix();
270 }
271
272 AString NiftiHeader::toString() const
273 {
274 AString ret;
275 if (isSwapped())
276 {
277 ret += "native endian: false\n";
278 } else {
279 ret += "native endian: true\n";
280 }
281 ret += "sizeof_hdr: " + AString_number(m_header.sizeof_hdr) + "\n";//skip the fields that aren't important, like intent_p1, cal_max, etc
282 ret += "magic: " + AString_from_latin1(m_header.magic, 8) + "\n";
283 ret += "datatype: " + AString_number(m_header.datatype) + "\n";
284 ret += "bitpix: " + AString_number(m_header.bitpix) + "\n";
285 CiftiAssert(m_header.dim[0] < 8);
286 for (int i = 0; i <= m_header.dim[0]; ++i)
287 {
288 ret += "dim[" + AString_number(i) + "]: " + AString_number(m_header.dim[i]) + "\n";
289 }
290 for (int i = 0; i <= m_header.dim[0]; ++i)
291 {
292 ret += "pixdim[" + AString_number(i) + "]: " + AString_number(m_header.pixdim[i]) + "\n";
293 }
294 ret += "vox_offset: " + AString_number(m_header.vox_offset) + "\n";
295 ret += "scl_slope: " + AString_number(m_header.scl_slope) + "\n";
296 ret += "scl_inter: " + AString_number(m_header.scl_inter) + "\n";
297 ret += "sform_code: " + AString_number(m_header.sform_code) + "\n";
298 if (m_header.sform_code != NIFTI_XFORM_UNKNOWN)
299 {
300 ret += "srow_x:";
301 for (int i = 0; i < 4; ++i)
302 {
303 ret += " " + AString_number(m_header.srow_x[i]);
304 }
305 ret += "\nsrow_y:";
306 for (int i = 0; i < 4; ++i)
307 {
308 ret += " " + AString_number(m_header.srow_y[i]);
309 }
310 ret += "\nsrow_z:";
311 for (int i = 0; i < 4; ++i)
312 {
313 ret += " " + AString_number(m_header.srow_z[i]);
314 }
315 ret += "\n";
316 }
317 ret += "qform_code: " + AString_number(m_header.qform_code) + "\n";
318 if (m_header.qform_code != NIFTI_XFORM_UNKNOWN)
319 {
320 ret += "quatern_b: " + AString_number(m_header.quatern_b) + "\n";
321 ret += "quatern_c: " + AString_number(m_header.quatern_c) + "\n";
322 ret += "quatern_d: " + AString_number(m_header.quatern_d) + "\n";
323 ret += "qoffset_x: " + AString_number(m_header.qoffset_x) + "\n";
324 ret += "qoffset_y: " + AString_number(m_header.qoffset_y) + "\n";
325 ret += "qoffset_z: " + AString_number(m_header.qoffset_z) + "\n";
326 }
327 ret += "xyzt_units: " + AString_number(m_header.xyzt_units) + "\n";
328 ret += "intent_code: " + AString_number(m_header.intent_code) + "\n";
329 ret += "intent_name: " + AString_from_latin1(m_header.intent_name, 16) + "\n";
330 int numExts = (int)m_extensions.size();
331 ret += AString_number(numExts) + " extension";
332 if (numExts != 1) ret += "s";
333 if (numExts == 0)
334 {
335 ret += "\n";
336 } else {
337 ret += ":\n";
338 for (int i = 0; i < numExts; ++i)
339 {
340 CiftiAssert(m_extensions[i] != NULL);
341 ret += "\n";
342 ret += "code: " + AString_number(m_extensions[i]->m_ecode) + "\n";
343 ret += "length: " + AString_number(m_extensions[i]->m_bytes.size()) + "\n";
344 }
345 }
346 return ret;
347 }
348
349 void NiftiHeader::setDataType(const int16_t& type)
350 {
351 m_header.bitpix = typeToNumBits(m_header.datatype);//to check for errors
352 m_header.datatype = type;
353 }
354
355 void NiftiHeader::setDimensions(const vector<int64_t>& dimsIn)
356 {
357 if (dimsIn.size() > 7 || dimsIn.empty()) throw CiftiException("Number of dimensions must be between 1 and 7, inclusive.");
358 m_header.dim[0] = dimsIn.size();
359 int i = 0;
360 for(; i < (int)dimsIn.size(); i++)
361 {
362 if (dimsIn[i] < 1) throw CiftiException("all dimension lengths must be positive");//maybe these should be asserts?
363 m_header.dim[i + 1] = dimsIn[i];
364 }
365 for (; i < 7; ++i)
366 {
367 m_header.dim[i + 1] = 1;//we maintain 1s on unused dimensions to make some stupid nifti readers happy
368 }
369 }
370
371 void NiftiHeader::setIntent(const int32_t& code, const char name[16])
372 {
373 m_header.intent_code = code;
374 int i;//custom strncpy-like code to fill nulls to the end
375 for (i = 0; i < 16 && name[i] != '\0'; ++i) m_header.intent_name[i] = name[i];
376 for (; i < 16; ++i) m_header.intent_name[i] = '\0';
377 }
378
379 void NiftiHeader::setSForm(const vector<vector<float> >& sForm)
380 {
381 CiftiAssert(sForm.size() >= 3);//programmer error to pass badly sized matrix
382 if (sForm.size() < 3) throw CiftiException("internal error: setSForm matrix badly sized");//but make release also throw
383 for (int i = 0; i < (int)sForm.size(); i++)
384 {
385 CiftiAssert(sForm[i].size() >= 4);//ditto
386 if (sForm[i].size() < 4) throw CiftiException("internal error: setSForm matrix badly sized");
387 }
388 m_header.xyzt_units = SPACE_TIME_TO_XYZT(NIFTI_UNITS_MM, NIFTI_UNITS_SEC);//overwrite whatever units we read in
389 for (int i = 0; i < 4; i++)
390 {
391 m_header.srow_x[i] = sForm[0][i];
392 m_header.srow_y[i] = sForm[1][i];
393 m_header.srow_z[i] = sForm[2][i];
394 }
395 m_header.sform_code = NIFTI_XFORM_SCANNER_ANAT;
396 Vector3D ivec, jvec, kvec;
397 ivec[0] = sForm[0][0]; ivec[1] = sForm[1][0]; ivec[2] = sForm[2][0];
398 jvec[0] = sForm[0][1]; jvec[1] = sForm[1][1]; jvec[2] = sForm[2][1];
399 kvec[0] = sForm[0][2]; kvec[1] = sForm[1][2]; kvec[2] = sForm[2][2];
400 m_header.pixdim[0] = 1.0f;
401 m_header.pixdim[1] = ivec.length();
402 m_header.pixdim[2] = jvec.length();
403 m_header.pixdim[3] = kvec.length();
404 ivec = ivec.normal();
405 jvec = jvec.normal();
406 kvec = kvec.normal();
407 if (kvec.dot(ivec.cross(jvec)) < 0.0f)//left handed sform!
408 {
409 m_header.pixdim[0] = -1.0f;
410 kvec = -kvec;//because to nifti, "left handed" apparently means "up is down", not "left is right"
411 }
412 float rotmat[3][3];
413 rotmat[0][0] = ivec[0]; rotmat[1][0] = jvec[0]; rotmat[2][0] = kvec[0];
414 rotmat[0][1] = ivec[1]; rotmat[1][1] = jvec[1]; rotmat[2][1] = kvec[1];
415 rotmat[0][2] = ivec[2]; rotmat[1][2] = jvec[2]; rotmat[2][2] = kvec[2];
416 float quat[4];
417 if (!MathFunctions::matrixToQuatern(rotmat, quat))
418 {
419 m_header.qform_code = NIFTI_XFORM_UNKNOWN;//0, implies that there is no qform
420 m_header.quatern_b = 0.0;//set dummy values anyway
421 m_header.quatern_c = 0.0;
422 m_header.quatern_d = 0.0;
423 m_header.qoffset_x = sForm[0][3];
424 m_header.qoffset_y = sForm[1][3];
425 m_header.qoffset_z = sForm[2][3];
426 } else {
427 m_header.qform_code = NIFTI_XFORM_SCANNER_ANAT;
428 m_header.quatern_b = quat[1];
429 m_header.quatern_c = quat[2];
430 m_header.quatern_d = quat[3];
431 m_header.qoffset_x = sForm[0][3];
432 m_header.qoffset_y = sForm[1][3];
433 m_header.qoffset_z = sForm[2][3];
434 }
435 }
436
437 void NiftiHeader::clearDataScaling()
438 {
439 m_header.scl_slope = 1.0;
440 m_header.scl_inter = 0.0;
441 }
442
443 void NiftiHeader::setDataScaling(const double& mult, const double& offset)
444 {
445 m_header.scl_slope = mult;
446 m_header.scl_inter = offset;
447 }
448
449 void NiftiHeader::read(BinaryFile& inFile)
450 {
451 nifti_1_header buffer1;
452 nifti_2_header buffer2;
453 inFile.read(&buffer1, sizeof(nifti_1_header));
454 int version = NIFTI2_VERSION(buffer1);
455 bool swapped = false;
456 try
457 {
458 if (version == 2)
459 {
460 memcpy(&buffer2, &buffer1, sizeof(nifti_1_header));
461 inFile.read(((char*)&buffer2) + sizeof(nifti_1_header), sizeof(nifti_2_header) - sizeof(nifti_1_header));
462 if (NIFTI2_NEEDS_SWAP(buffer2))
463 {
464 swapped = true;
465 swapHeaderBytes(buffer2);
466 }
467 setupFrom(buffer2);
468 } else if (version == 1) {
469 if (NIFTI2_NEEDS_SWAP(buffer1))//yes, this works on nifti-1 also
470 {
471 swapped = true;
472 swapHeaderBytes(buffer1);
473 }
474 setupFrom(buffer1);
475 } else {
476 throw CiftiException(inFile.getFilename() + " is not a valid NIfTI file");
477 }
478 } catch (CiftiException& e) {//catch and throw in order to add filename info
479 throw CiftiException("error reading NIfTI file " + inFile.getFilename() + ": " + e.whatString());
480 }
481 m_extensions.clear();
482 char extender[4];
483 inFile.read(extender, 4);
484 int extensions = 0;//if it has extensions in a format we don't know about, don't try to read them
485 if (version == 1 && extender[0] != 0) extensions = 1;//sadly, this is the only thing nifti-1 says about the extender bytes
486 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:
487 if (extensions == 1)//"extentions match those of NIfTI-1.1 when the extender bytes are 1 0 0 0"
488 {
489 int64_t extStart;
490 if (version == 1)
491 {
492 extStart = 352;
493 } else {
494 CiftiAssert(version == 2);
495 extStart = 544;
496 }
497 CiftiAssert(inFile.pos() == extStart);
498 while(extStart + 2 * sizeof(int32_t) <= (size_t)m_header.vox_offset)
499 {
500 int32_t esize, ecode;
501 inFile.read(&esize, sizeof(int32_t));
502 if (swapped) ByteSwapping::swap(esize);
503 inFile.read(&ecode, sizeof(int32_t));
504 if (swapped) ByteSwapping::swap(ecode);
505 if (esize < 8 || esize + extStart > m_header.vox_offset) break;
506 boost::shared_ptr<NiftiExtension> tempExtension(new NiftiExtension());
507 if ((size_t)esize > 2 * sizeof(int32_t))//don't try to read 0 bytes
508 {
509 tempExtension->m_bytes.resize(esize - 2 * sizeof(int32_t));
510 inFile.read(tempExtension->m_bytes.data(), esize - 2 * sizeof(int32_t));
511 }
512 tempExtension->m_ecode = ecode;
513 m_extensions.push_back(tempExtension);
514 extStart += esize;//esize includes the two int32_ts
515 }
516 }
517 m_isSwapped = swapped;//now that we know there were no errors (because they throw), complete the internal state
518 m_version = version;
519 }
520
521 void NiftiHeader::setupFrom(const nifti_1_header& header)
522 {
523 if (header.sizeof_hdr != sizeof(nifti_1_header)) throw CiftiException("incorrect sizeof_hdr");
524 const char magic[] = "n+1\0";//only support single-file nifti
525 if (strncmp(header.magic, magic, 4) != 0) throw CiftiException("incorrect magic");
526 if (header.dim[0] < 1 || header.dim[0] > 7) throw CiftiException("incorrect dim[0]");
527 for (int i = 0; i < header.dim[0]; ++i)
528 {
529 if (header.dim[i + 1] < 1) throw CiftiException("dim[" + AString_number(i + 1) + "] < 1");
530 }
531 if (header.vox_offset < 352) throw CiftiException("incorrect vox_offset");
532 int numBits = typeToNumBits(header.datatype);
533 if (header.bitpix != numBits) throw CiftiException("datatype disagrees with bitpix");
534 m_header.sizeof_hdr = header.sizeof_hdr;//copy in everything, so we don't have to fake anything to print the header as read
535 for (int i = 0; i < 4; ++i)//mostly using nifti-2 field order to make it easier to find if things are missed
536 {
537 m_header.magic[i] = header.magic[i];
538 m_header.srow_x[i] = header.srow_x[i];//slight hack - nifti-1 magic and srows both happen to be 4 long
539 m_header.srow_y[i] = header.srow_y[i];
540 m_header.srow_z[i] = header.srow_z[i];
541 }
542 m_header.datatype = header.datatype;
543 m_header.bitpix = header.bitpix;
544 for (int i = 0; i < 8; ++i)
545 {
546 m_header.dim[i] = header.dim[i];
547 m_header.pixdim[i] = header.pixdim[i];
548 }
549 m_header.intent_p1 = header.intent_p1;
550 m_header.intent_p2 = header.intent_p2;
551 m_header.intent_p3 = header.intent_p3;
552 m_header.vox_offset = header.vox_offset;//technically, this could result in integer overflow, if the header extensions total exabytes in size
553 m_header.scl_slope = header.scl_slope;
554 m_header.scl_inter = header.scl_inter;
555 m_header.cal_max = header.cal_max;
556 m_header.cal_min = header.cal_min;
557 m_header.slice_duration = header.slice_duration;
558 m_header.toffset = header.toffset;
559 m_header.slice_start = header.slice_start;
560 m_header.slice_end = header.slice_end;
561 for (int i = 0; i < 80; ++i) m_header.descrip[i] = header.descrip[i];
562 for (int i = 0; i < 24; ++i) m_header.aux_file[i] = header.aux_file[i];
563 m_header.qform_code = header.qform_code;
564 m_header.sform_code = header.sform_code;
565 m_header.quatern_b = header.quatern_b;
566 m_header.quatern_c = header.quatern_c;
567 m_header.quatern_d = header.quatern_d;
568 m_header.qoffset_x = header.qoffset_x;
569 m_header.qoffset_y = header.qoffset_y;
570 m_header.qoffset_z = header.qoffset_z;
571 m_header.slice_code = header.slice_code;
572 m_header.xyzt_units = header.xyzt_units;
573 m_header.intent_code = header.intent_code;
574 for (int i = 0; i < 16; ++i) m_header.intent_name[i] = header.intent_name[i];
575 m_header.dim_info = header.dim_info;
576 }
577
578 void NiftiHeader::setupFrom(const nifti_2_header& header)
579 {
580 if (header.sizeof_hdr != sizeof(nifti_2_header)) throw CiftiException("incorrect sizeof_hdr");
581 const char magic[] = "n+2\0\r\n\032\n";//only support single-file nifti
582 for (int i = 0; i < 8; ++i)
583 {
584 if (header.magic[i] != magic[i]) throw CiftiException("incorrect magic");
585 }
586 if (header.dim[0] < 1 || header.dim[0] > 7) throw CiftiException("incorrect dim[0]");
587 for (int i = 0; i < header.dim[0]; ++i)
588 {
589 if (header.dim[i + 1] < 1) throw CiftiException("dim[" + AString_number(i + 1) + "] < 1");
590 }
591 if (header.vox_offset < 352) throw CiftiException("incorrect vox_offset");
592 if (header.bitpix != typeToNumBits(header.datatype)) throw CiftiException("datatype disagrees with bitpix");
593 memcpy(&m_header, &header, sizeof(nifti_2_header));
594 }
595
596 int NiftiHeader::typeToNumBits(const int64_t& type)
597 {
598 switch (type)
599 {
600 case DT_BINARY:
601 return 1;
602 break;
603 case NIFTI_TYPE_INT8:
604 case NIFTI_TYPE_UINT8:
605 return 8;
606 break;
607 case NIFTI_TYPE_INT16:
608 case NIFTI_TYPE_UINT16:
609 return 16;
610 break;
611 case NIFTI_TYPE_RGB24:
612 return 24;
613 break;
614 case NIFTI_TYPE_INT32:
615 case NIFTI_TYPE_UINT32:
616 case NIFTI_TYPE_FLOAT32:
617 return 32;
618 break;
619 case NIFTI_TYPE_INT64:
620 case NIFTI_TYPE_UINT64:
621 case NIFTI_TYPE_FLOAT64:
622 case NIFTI_TYPE_COMPLEX64:
623 return 64;
624 break;
625 case NIFTI_TYPE_FLOAT128:
626 case NIFTI_TYPE_COMPLEX128:
627 return 128;
628 break;
629 case NIFTI_TYPE_COMPLEX256:
630 return 256;
631 break;
632 default:
633 throw CiftiException("incorrect datatype code");
634 }
635 }
636
637 void NiftiHeader::swapHeaderBytes(nifti_1_header& header)
638 {
639 ByteSwapping::swap(header.sizeof_hdr);//by order of fields in nifti-1 header, skip unused because we don't store their data
640 ByteSwapping::swapArray(header.dim, 8);
641 ByteSwapping::swap(header.intent_p1);
642 ByteSwapping::swap(header.intent_p2);
643 ByteSwapping::swap(header.intent_p3);
644 ByteSwapping::swap(header.intent_code);
645 ByteSwapping::swap(header.datatype);
646 ByteSwapping::swap(header.bitpix);
647 ByteSwapping::swap(header.slice_start);
648 ByteSwapping::swapArray(header.pixdim, 8);
649 ByteSwapping::swap(header.vox_offset);
650 ByteSwapping::swap(header.scl_slope);
651 ByteSwapping::swap(header.scl_inter);
652 ByteSwapping::swap(header.slice_end);
653 ByteSwapping::swap(header.cal_max);
654 ByteSwapping::swap(header.cal_min);
655 ByteSwapping::swap(header.slice_duration);
656 ByteSwapping::swap(header.toffset);
657 ByteSwapping::swap(header.qform_code);
658 ByteSwapping::swap(header.sform_code);
659 ByteSwapping::swap(header.quatern_b);
660 ByteSwapping::swap(header.quatern_c);
661 ByteSwapping::swap(header.quatern_d);
662 ByteSwapping::swap(header.qoffset_x);
663 ByteSwapping::swap(header.qoffset_y);
664 ByteSwapping::swap(header.qoffset_z);
665 ByteSwapping::swapArray(header.srow_x, 4);
666 ByteSwapping::swapArray(header.srow_y, 4);
667 ByteSwapping::swapArray(header.srow_z, 4);
668 }
669
670 void NiftiHeader::swapHeaderBytes(nifti_2_header& header)
671 {
672 ByteSwapping::swap(header.sizeof_hdr);//by order of fields in nifti-2 header
673 ByteSwapping::swap(header.datatype);
674 ByteSwapping::swap(header.bitpix);
675 ByteSwapping::swapArray(header.dim, 8);
676 ByteSwapping::swap(header.intent_p1);
677 ByteSwapping::swap(header.intent_p2);
678 ByteSwapping::swap(header.intent_p3);
679 ByteSwapping::swapArray(header.pixdim, 8);
680 ByteSwapping::swap(header.vox_offset);
681 ByteSwapping::swap(header.scl_slope);
682 ByteSwapping::swap(header.scl_inter);
683 ByteSwapping::swap(header.cal_max);
684 ByteSwapping::swap(header.cal_min);
685 ByteSwapping::swap(header.slice_duration);
686 ByteSwapping::swap(header.toffset);
687 ByteSwapping::swap(header.slice_start);
688 ByteSwapping::swap(header.slice_end);
689 ByteSwapping::swap(header.qform_code);
690 ByteSwapping::swap(header.sform_code);
691 ByteSwapping::swap(header.quatern_b);
692 ByteSwapping::swap(header.quatern_c);
693 ByteSwapping::swap(header.quatern_d);
694 ByteSwapping::swap(header.qoffset_x);
695 ByteSwapping::swap(header.qoffset_y);
696 ByteSwapping::swap(header.qoffset_z);
697 ByteSwapping::swapArray(header.srow_x, 4);
698 ByteSwapping::swapArray(header.srow_y, 4);
699 ByteSwapping::swapArray(header.srow_z, 4);
700 ByteSwapping::swap(header.slice_code);
701 ByteSwapping::swap(header.xyzt_units);
702 ByteSwapping::swap(header.intent_code);
703 }
704
705 void NiftiHeader::write(BinaryFile& outFile, const int& version, const bool& swapEndian)
706 {
707 if (!canWriteVersion(version)) throw CiftiException("unable to write NIfTI version " + AString_number(version) + " for file " + outFile.getFilename());
708 const char padding[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
709 int64_t voxOffset;
710 if (version == 2)
711 {
712 nifti_2_header outHeader;
713 prepareHeader(outHeader);
714 voxOffset = outHeader.vox_offset;
715 if (swapEndian) swapHeaderBytes(outHeader);
716 outFile.write(&outHeader, sizeof(nifti_2_header));
717 } else if (version == 1) {
718 nifti_1_header outHeader;
719 prepareHeader(outHeader);
720 voxOffset = outHeader.vox_offset;
721 if (swapEndian) swapHeaderBytes(outHeader);
722 outFile.write(&outHeader, sizeof(nifti_1_header));
723 } else {
724 CiftiAssert(0);//canWriteVersion should have said no
725 throw CiftiException("internal error: NiftiHeader::canWriteVersion() returned true for unimplemented writing version");
726 }
727 char extender[] = { 0, 0, 0, 0 };//at least until nifti-2 gets a new extension format, use the same code for both
728 int numExtensions = (int)m_extensions.size();
729 if (numExtensions != 0) extender[0] = 1;
730 outFile.write(extender, 4);
731 for (int i = 0; i < numExtensions; ++i)
732 {
733 CiftiAssert(m_extensions[i] != NULL);
734 int64_t thisSize = 8 + m_extensions[i]->m_bytes.size();//8 is for the int32_t size and ecode for nifti-1 style extensions
735 int paddingBytes = 0;
736 if (thisSize % 16 != 0)//round up to nearest multiple of 16
737 {
738 paddingBytes = 16 - (thisSize % 16);
739 thisSize += paddingBytes;
740 }
741 CiftiAssert(thisSize <= numeric_limits<int32_t>::max());
742 CiftiAssert(thisSize + outFile.pos() <= voxOffset);
743 int32_t outSize = thisSize;
744 int32_t outEcode = m_extensions[i]->m_ecode;
745 if (swapEndian)
746 {
747 ByteSwapping::swap(outSize);
748 ByteSwapping::swap(outEcode);
749 }
750 outFile.write(&outSize, sizeof(int32_t));
751 outFile.write(&outEcode, sizeof(int32_t));
752 outFile.write(m_extensions[i]->m_bytes.data(), m_extensions[i]->m_bytes.size());
753 if (paddingBytes != 0) outFile.write(padding, paddingBytes);
754 }
755 CiftiAssert(outFile.pos() == voxOffset);
756 m_header.vox_offset = voxOffset;//update internal state to reflect the state that was written to the file
757 m_version = version;
758 m_isSwapped = swapEndian;
759 }
760
761 void NiftiHeader::prepareHeader(nifti_1_header& header) const
762 {
763 CiftiAssert(canWriteVersion(1));//programmer error to call this if it isn't possible
764 header.sizeof_hdr = sizeof(nifti_1_header);//do static things first
765 const char magic[] = "n+1\0";//only support single-file nifti
766 for (int i = 0; i < 4; ++i) header.magic[i] = magic[i];
767 for (int i = 0; i < 10; ++i) header.data_type[i] = 0;//then zero unused things
768 for (int i = 0; i < 18; ++i) header.db_name[i] = 0;
769 header.extents = 0;
770 header.session_error = 0;
771 header.regular = 0;
772 header.glmax = 0;
773 header.glmin = 0;
774 header.dim_info = m_header.dim_info;//by order of fields in nifti-1 header, skipping unused and static
775 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
776 header.intent_p1 = m_header.intent_p1;//theoretically, this could be a problem wih large exponents, or if extremely high precision is required
777 header.intent_p2 = m_header.intent_p2;//but we don't use them at all currently, so we don't care
778 header.intent_p3 = m_header.intent_p3;
779 header.intent_code = m_header.intent_code;
780 header.datatype = m_header.datatype;
781 header.bitpix = typeToNumBits(m_header.datatype);//in case we ever accept wrong bitpix with a warning, NEVER write wrong bitpix
782 header.slice_start = m_header.slice_start;
783 for (int i = 0; i < 8; ++i) header.pixdim[i] = m_header.pixdim[i];//more double to float conversion
784 header.vox_offset = computeVoxOffset(1);//again, canWriteVersion should have checked that this, and later conversions, are okay
785 CiftiAssert(header.vox_offset >= 352);
786 header.scl_slope = m_header.scl_slope;
787 header.scl_inter = m_header.scl_inter;
788 header.slice_end = m_header.slice_end;
789 header.slice_code = m_header.slice_code;
790 header.xyzt_units = m_header.xyzt_units;
791 header.cal_min = m_header.cal_min;
792 header.cal_max = m_header.cal_max;
793 header.slice_duration = m_header.slice_duration;
794 header.toffset = m_header.toffset;
795 for (int i = 0; i < 80; ++i) header.descrip[i] = m_header.descrip[i];
796 for (int i = 0; i < 24; ++i) header.aux_file[i] = m_header.aux_file[i];
797 header.qform_code = m_header.qform_code;
798 header.sform_code = m_header.sform_code;
799 header.quatern_b = m_header.quatern_b;
800 header.quatern_c = m_header.quatern_c;
801 header.quatern_d = m_header.quatern_d;
802 header.qoffset_x = m_header.qoffset_x;
803 header.qoffset_y = m_header.qoffset_y;
804 header.qoffset_z = m_header.qoffset_z;
805 for (int i = 0; i < 4; ++i)
806 {
807 header.srow_x[i] = m_header.srow_x[i];
808 header.srow_y[i] = m_header.srow_y[i];
809 header.srow_z[i] = m_header.srow_z[i];
810 }
811 for (int i = 0; i < 16; ++i) header.intent_name[i] = m_header.intent_name[i];
812 }
813
814 void NiftiHeader::prepareHeader(nifti_2_header& header) const
815 {
816 CiftiAssert(canWriteVersion(2));
817 memcpy(&header, &m_header, sizeof(nifti_2_header));//first copy everything, then fix static and computed fields
818 header.sizeof_hdr = sizeof(nifti_2_header);
819 const char magic[] = "n+2\0\r\n\032\n";
820 for (int i = 0; i < 8; ++i) header.magic[i] = magic[i];
821 header.bitpix = typeToNumBits(header.datatype);
822 header.vox_offset = computeVoxOffset(2);
823 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
824 }
0 #ifndef __NIFTI_HEADER_H__
1 #define __NIFTI_HEADER_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/BinaryFile.h"
31
32 #include "nifti1.h"
33 #include "nifti2.h"
34
35 #include "boost/shared_ptr.hpp"
36 #include <vector>
37
38 namespace cifti
39 {
40
41 struct NiftiExtension
42 {
43 int32_t m_ecode;
44 std::vector<char> m_bytes;
45 };
46
47 struct NiftiHeader
48 {
49 std::vector<boost::shared_ptr<NiftiExtension> > m_extensions;//allow direct access to the extensions
50
51 NiftiHeader();
52 void read(BinaryFile& inFile);
53 void write(BinaryFile& outFile, const int& version = 1, const bool& swapEndian = false);
54 bool canWriteVersion(const int& version) const;
55 bool isSwapped() const { return m_isSwapped; }
56 int version() const { return m_version; }
57
58 std::vector<int64_t> getDimensions() const;
59 std::vector<std::vector<float> > getSForm() const;
60 int64_t getDataOffset() const { return m_header.vox_offset; }
61 int16_t getDataType() const { return m_header.datatype; }
62 int32_t getIntentCode() const { return m_header.intent_code; }
63 const char* getIntentName() const { return m_header.intent_name; }//NOTE: 16 BYTES, MAY NOT HAVE A NULL TERMINATOR
64 bool getDataScaling(double& mult, double& offset) const;//returns false if scaling not needed
65 AString toString() const;
66
67 void setDimensions(const std::vector<int64_t>& dimsIn);
68 void setSForm(const std::vector<std::vector<float> > &sForm);
69 void setIntent(const int32_t& code, const char name[16]);
70 void setDataType(const int16_t& type);
71 void clearDataScaling();
72 void setDataScaling(const double& mult, const double& offset);
73 ///get the FSL "scale" space
74 std::vector<std::vector<float> > getFSLSpace() const;
75
76 bool operator==(const NiftiHeader& rhs) const;//for testing purposes
77 bool operator!=(const NiftiHeader& rhs) const { return !((*this) == rhs); }
78 private:
79 nifti_2_header m_header;//storage for header values regardless of version
80 int m_version;
81 bool m_isSwapped;
82 static void swapHeaderBytes(nifti_1_header &header);
83 static void swapHeaderBytes(nifti_2_header &header);
84 void prepareHeader(nifti_1_header& header) const;//transform internal state into ready to write header struct
85 void prepareHeader(nifti_2_header& header) const;
86 void setupFrom(const nifti_1_header& header);//error check provided header, and populate members from it
87 void setupFrom(const nifti_2_header& header);
88 static int typeToNumBits(const int64_t& type);
89 int64_t computeVoxOffset(const int& version) const;
90 };
91
92 }
93
94 #endif //__NIFTI_HEADER_H__
0 #ifndef _NIFTI_HEADER_
1 #define _NIFTI_HEADER_
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 /*
30 * NOTE: our version is somewhat modified, turning constant #defines into const int32_t, namespacing, removal of some other #defines, to try to coexist with the original header
31 */
32 namespace cifti
33 {
34
35 /*****************************************************************************
36 ** This file defines the "NIFTI-1" header format. **
37 ** It is derived from 2 meetings at the NIH (31 Mar 2003 and **
38 ** 02 Sep 2003) of the Data Format Working Group (DFWG), **
39 ** chartered by the NIfTI (Neuroimaging Informatics Technology **
40 ** Initiative) at the National Institutes of Health (NIH). **
41 **--------------------------------------------------------------**
42 ** Neither the National Institutes of Health (NIH), the DFWG, **
43 ** nor any of the members or employees of these institutions **
44 ** imply any warranty of usefulness of this material for any **
45 ** purpose, and do not assume any liability for damages, **
46 ** incidental or otherwise, caused by any use of this document. **
47 ** If these conditions are not acceptable, do not use this! **
48 **--------------------------------------------------------------**
49 ** Author: Robert W Cox (NIMH, Bethesda) **
50 ** Advisors: John Ashburner (FIL, London), **
51 ** Stephen Smith (FMRIB, Oxford), **
52 ** Mark Jenkinson (FMRIB, Oxford) **
53 ******************************************************************************/
54
55 /*---------------------------------------------------------------------------*/
56 /* Note that the ANALYZE 7.5 file header (dbh.h) is
57 (c) Copyright 1986-1995
58 Biomedical Imaging Resource
59 Mayo Foundation
60 Incorporation of components of dbh.h are by permission of the
61 Mayo Foundation.
62
63 Changes from the ANALYZE 7.5 file header in this file are released to the
64 public domain, including the functional comments and any amusing asides.
65 -----------------------------------------------------------------------------*/
66
67 /*---------------------------------------------------------------------------*/
68 /*! INTRODUCTION TO NIFTI-1:
69 ------------------------
70 The twin (and somewhat conflicting) goals of this modified ANALYZE 7.5
71 format are:
72 (a) To add information to the header that will be useful for functional
73 neuroimaging data analysis and display. These additions include:
74 - More basic data types.
75 - Two affine transformations to specify voxel coordinates.
76 - "Intent" codes and parameters to describe the meaning of the data.
77 - Affine scaling of the stored data values to their "true" values.
78 - Optional storage of the header and image data in one file (.nii).
79 (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible
80 software (i.e., such a program should be able to do something useful
81 with a NIFTI-1 dataset -- at least, with one stored in a traditional
82 .img/.hdr file pair).
83
84 Most of the unused fields in the ANALYZE 7.5 header have been taken,
85 and some of the lesser-used fields have been co-opted for other purposes.
86 Notably, most of the data_history substructure has been co-opted for
87 other purposes, since the ANALYZE 7.5 format describes this substructure
88 as "not required".
89
90 NIFTI-1 FLAG (MAGIC STRINGS):
91 ----------------------------
92 To flag such a struct as being conformant to the NIFTI-1 spec, the last 4
93 bytes of the header must be either the C String "ni1" or "n+1";
94 in hexadecimal, the 4 bytes
95 6E 69 31 00 or 6E 2B 31 00
96 (in any future version of this format, the '1' will be upgraded to '2',
97 etc.). Normally, such a "magic number" or flag goes at the start of the
98 file, but trying to avoid clobbering widely-used ANALYZE 7.5 fields led to
99 putting this marker last. However, recall that "the last shall be first"
100 (Matthew 20:16).
101
102 If a NIFTI-aware program reads a header file that is NOT marked with a
103 NIFTI magic string, then it should treat the header as an ANALYZE 7.5
104 structure.
105
106 NIFTI-1 FILE STORAGE:
107 --------------------
108 "ni1" means that the image data is stored in the ".img" file corresponding
109 to the header file (starting at file offset 0).
110
111 "n+1" means that the image data is stored in the same file as the header
112 information. We recommend that the combined header+data filename suffix
113 be ".nii". When the dataset is stored in one file, the first byte of image
114 data is stored at byte location (int)vox_offset in this combined file.
115 The minimum allowed value of vox_offset is 352; for compatibility with
116 some software, vox_offset should be an integral multiple of 16.
117
118 GRACE UNDER FIRE:
119 ----------------
120 Most NIFTI-aware programs will only be able to handle a subset of the full
121 range of datasets possible with this format. All NIFTI-aware programs
122 should take care to check if an input dataset conforms to the program's
123 needs and expectations (e.g., check datatype, intent_code, etc.). If the
124 input dataset can't be handled by the program, the program should fail
125 gracefully (e.g., print a useful warning; not crash).
126
127 SAMPLE CODES:
128 ------------
129 The associated files nifti1_io.h and nifti1_io.c provide a sample
130 implementation in C of a set of functions to read, write, and manipulate
131 NIFTI-1 files. The file nifti1_test.c is a sample program that uses
132 the nifti1_io.c functions.
133 -----------------------------------------------------------------------------*/
134
135 /*---------------------------------------------------------------------------*/
136 /* HEADER STRUCT DECLARATION:
137 -------------------------
138 In the comments below for each field, only NIFTI-1 specific requirements
139 or changes from the ANALYZE 7.5 format are described. For convenience,
140 the 348 byte header is described as a single struct, rather than as the
141 ANALYZE 7.5 group of 3 substructs.
142
143 Further comments about the interpretation of various elements of this
144 header are after the data type definition itself. Fields that are
145 marked as ++UNUSED++ have no particular interpretation in this standard.
146 (Also see the UNUSED FIELDS comment section, far below.)
147
148 The presumption below is that the various C types have particular sizes:
149 sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2
150 -----------------------------------------------------------------------------*/
151
152 //hopefully cross-platform solution to byte padding added by some compilers
153 #pragma pack(push)
154 #pragma pack(1)
155
156 /*! \struct nifti_1_header
157 \brief Data structure defining the fields in the nifti1 header.
158 This binary header should be found at the beginning of a valid
159 NIFTI-1 header file.
160 */
161 /*************************/ /************************/
162 struct nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */
163 /*************************/ /************************/
164
165 /*--- was header_key substruct ---*/
166 int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ /* 0 */
167 char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ /* 4 */
168 char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ /* 14 */
169 int extents; /*!< ++UNUSED++ */ /* int extents; */ /* 32 */
170 short session_error; /*!< ++UNUSED++ */ /* short session_error; */ /* 36 */
171 char regular; /*!< ++UNUSED++ */ /* char regular; */ /* 38 */
172 char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ /* 39 */
173
174 /*--- was image_dimension substruct ---*/
175 short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ /* 40 */
176 float intent_p1 ; /*!< 1st intent parameter. */ /* short unused8; */ /* 56 */
177 /* short unused9; */
178 float intent_p2 ; /*!< 2nd intent parameter. */ /* short unused10; */ /* 60 */
179 /* short unused11; */
180 float intent_p3 ; /*!< 3rd intent parameter. */ /* short unused12; */ /* 64 */
181 /* short unused13; */
182 short intent_code ; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ /* 68 */
183 short datatype; /*!< Defines data type! */ /* short datatype; */ /* 70 */
184 short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ /* 72 */
185 short slice_start; /*!< First slice index. */ /* short dim_un0; */ /* 74 */
186 float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ /* 76 */
187 float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ /* 108 */
188 float scl_slope ; /*!< Data scaling: slope. */ /* float funused1; */ /* 112 */
189 float scl_inter ; /*!< Data scaling: offset. */ /* float funused2; */ /* 116 */
190 short slice_end; /*!< Last slice index. */ /* float funused3; */ /* 120 */
191 char slice_code ; /*!< Slice timing order. */ /* 122 */
192 char xyzt_units ; /*!< Units of pixdim[1..4] */ /* 123 */
193 float cal_max; /*!< Max display intensity */ /* float cal_max; */ /* 124 */
194 float cal_min; /*!< Min display intensity */ /* float cal_min; */ /* 128 */
195 float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ /* 132 */
196 float toffset; /*!< Time axis shift. */ /* float verified; */ /* 136 */
197 int glmax; /*!< ++UNUSED++ */ /* int glmax; */ /* 140 */
198 int glmin; /*!< ++UNUSED++ */ /* int glmin; */ /* 144 */
199
200 /*--- was data_history substruct ---*/
201 char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ /* 148 */
202 char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ /* 228 */
203
204 short qform_code ; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ /* 252 */
205 short sform_code ; /*!< NIFTI_XFORM_* code. */ /* fields below here */ /* 254 */
206 /* are replaced */
207 float quatern_b ; /*!< Quaternion b param. */ /* 256 */
208 float quatern_c ; /*!< Quaternion c param. */ /* 260 */
209 float quatern_d ; /*!< Quaternion d param. */ /* 264 */
210 float qoffset_x ; /*!< Quaternion x shift. */ /* 268 */
211 float qoffset_y ; /*!< Quaternion y shift. */ /* 272 */
212 float qoffset_z ; /*!< Quaternion z shift. */ /* 276 */
213
214 float srow_x[4] ; /*!< 1st row affine transform. */ /* 280 */
215 float srow_y[4] ; /*!< 2nd row affine transform. */ /* 296 */
216 float srow_z[4] ; /*!< 3rd row affine transform. */ /* 312 */
217
218 char intent_name[16];/*!< 'name' or meaning of data. */ /* 328 */
219
220 char magic[4] ; /*!< MUST be "ni1\0" or "n+1\0". */ /* 344 */
221
222 } ; /**** 348 bytes total ****/
223
224 typedef struct nifti_1_header nifti_1_header ;
225
226 /*---------------------------------------------------------------------------*/
227 /* HEADER EXTENSIONS:
228 -----------------
229 After the end of the 348 byte header (e.g., after the magic field),
230 the next 4 bytes are a char array field named "extension". By default,
231 all 4 bytes of this array should be set to zero. In a .nii file, these
232 4 bytes will always be present, since the earliest start point for
233 the image data is byte #352. In a separate .hdr file, these bytes may
234 or may not be present. If not present (i.e., if the length of the .hdr
235 file is 348 bytes), then a NIfTI-1 compliant program should use the
236 default value of extension={0,0,0,0}. The first byte (extension[0])
237 is the only value of this array that is specified at present. The other
238 3 bytes are reserved for future use.
239
240 If extension[0] is nonzero, it indicates that extended header information
241 is present in the bytes following the extension array. In a .nii file,
242 this extended header data is before the image data (and vox_offset
243 must be set correctly to allow for this). In a .hdr file, this extended
244 data follows extension and proceeds (potentially) to the end of the file.
245
246 The format of extended header data is weakly specified. Each extension
247 must be an integer multiple of 16 bytes long. The first 8 bytes of each
248 extension comprise 2 integers:
249 int esize , ecode ;
250 These values may need to be byte-swapped, as indicated by dim[0] for
251 the rest of the header.
252 * esize is the number of bytes that form the extended header data
253 + esize must be a positive integral multiple of 16
254 + this length includes the 8 bytes of esize and ecode themselves
255 * ecode is a non-negative integer that indicates the format of the
256 extended header data that follows
257 + different ecode values are assigned to different developer groups
258 + at present, the "registered" values for code are
259 = 0 = unknown private format (not recommended!)
260 = 2 = DICOM format (i.e., attribute tags and values)
261 = 4 = AFNI group (i.e., ASCII XML-ish elements)
262 In the interests of interoperability (a primary rationale for NIfTI),
263 groups developing software that uses this extension mechanism are
264 encouraged to document and publicize the format of their extensions.
265 To this end, the NIfTI DFWG will assign even numbered codes upon request
266 to groups submitting at least rudimentary documentation for the format
267 of their extension; at present, the contact is mailto:rwcox@nih.gov.
268 The assigned codes and documentation will be posted on the NIfTI
269 website. All odd values of ecode (and 0) will remain unassigned;
270 at least, until the even ones are used up, when we get to 2,147,483,646.
271
272 Note that the other contents of the extended header data section are
273 totally unspecified by the NIfTI-1 standard. In particular, if binary
274 data is stored in such a section, its byte order is not necessarily
275 the same as that given by examining dim[0]; it is incumbent on the
276 programs dealing with such data to determine the byte order of binary
277 extended header data.
278
279 Multiple extended header sections are allowed, each starting with an
280 esize,ecode value pair. The first esize value, as described above,
281 is at bytes #352-355 in the .hdr or .nii file (files start at byte #0).
282 If this value is positive, then the second (esize2) will be found
283 starting at byte #352+esize1 , the third (esize3) at byte #352+esize1+esize2,
284 et cetera. Of course, in a .nii file, the value of vox_offset must
285 be compatible with these extensions. If a malformed file indicates
286 that an extended header data section would run past vox_offset, then
287 the entire extended header section should be ignored. In a .hdr file,
288 if an extended header data section would run past the end-of-file,
289 that extended header data should also be ignored.
290
291 With the above scheme, a program can successively examine the esize
292 and ecode values, and skip over each extended header section if the
293 program doesn't know how to interpret the data within. Of course, any
294 program can simply ignore all extended header sections simply by jumping
295 straight to the image data using vox_offset.
296 -----------------------------------------------------------------------------*/
297
298 /*! \struct nifti1_extender
299 \brief This structure represents a 4-byte string that should follow the
300 binary nifti_1_header data in a NIFTI-1 header file. If the char
301 values are {1,0,0,0}, the file is expected to contain extensions,
302 values of {0,0,0,0} imply the file does not contain extensions.
303 Other sequences of values are not currently defined.
304 */
305 struct nifti1_extender { char extension[4] ; } ;
306 typedef struct nifti1_extender nifti1_extender ;
307
308 /*! \struct nifti1_extension
309 \brief Data structure defining the fields of a header extension.
310 */
311 struct nifti1_extension {
312 int esize ; /*!< size of extension, in bytes (must be multiple of 16) */
313 int ecode ; /*!< extension code, one of the NIFTI_ECODE_ values */
314 char * edata ; /*!< raw data, with no byte swapping */
315 } ;
316 typedef struct nifti1_extension nifti1_extension ;
317
318 //and restore packing behavior
319 #pragma pack(pop)
320
321 /*---------------------------------------------------------------------------*/
322 /* DATA DIMENSIONALITY (as in ANALYZE 7.5):
323 ---------------------------------------
324 dim[0] = number of dimensions;
325 - if dim[0] is outside range 1..7, then the header information
326 needs to be byte swapped appropriately
327 - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves
328 dimensions 1,2,3 for space (x,y,z), 4 for time (t), and
329 5,6,7 for anything else needed.
330
331 dim[i] = length of dimension #i, for i=1..dim[0] (must be positive)
332 - also see the discussion of intent_code, far below
333
334 pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive)
335 - cf. ORIENTATION section below for use of pixdim[0]
336 - the units of pixdim can be specified with the xyzt_units
337 field (also described far below).
338
339 Number of bits per voxel value is in bitpix, which MUST correspond with
340 the datatype field. The total number of bytes in the image data is
341 dim[1] * ... * dim[dim[0]] * bitpix / 8
342
343 In NIFTI-1 files, dimensions 1,2,3 are for space, dimension 4 is for time,
344 and dimension 5 is for storing multiple values at each spatiotemporal
345 voxel. Some examples:
346 - A typical whole-brain FMRI experiment's time series:
347 - dim[0] = 4
348 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM
349 - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC
350 - dim[3] = 20 pixdim[3] = 5.0
351 - dim[4] = 120 pixdim[4] = 2.0
352 - A typical T1-weighted anatomical volume:
353 - dim[0] = 3
354 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM
355 - dim[2] = 256 pixdim[2] = 1.0
356 - dim[3] = 128 pixdim[3] = 1.1
357 - A single slice EPI time series:
358 - dim[0] = 4
359 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM
360 - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC
361 - dim[3] = 1 pixdim[3] = 5.0
362 - dim[4] = 1200 pixdim[4] = 0.2
363 - A 3-vector stored at each point in a 3D volume:
364 - dim[0] = 5
365 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM
366 - dim[2] = 256 pixdim[2] = 1.0
367 - dim[3] = 128 pixdim[3] = 1.1
368 - dim[4] = 1 pixdim[4] = 0.0
369 - dim[5] = 3 intent_code = NIFTI_INTENT_VECTOR
370 - A single time series with a 3x3 matrix at each point:
371 - dim[0] = 5
372 - dim[1] = 1 xyzt_units = NIFTI_UNITS_SEC
373 - dim[2] = 1
374 - dim[3] = 1
375 - dim[4] = 1200 pixdim[4] = 0.2
376 - dim[5] = 9 intent_code = NIFTI_INTENT_GENMATRIX
377 - intent_p1 = intent_p2 = 3.0 (indicates matrix dimensions)
378 -----------------------------------------------------------------------------*/
379
380 /*---------------------------------------------------------------------------*/
381 /* DATA STORAGE:
382 ------------
383 If the magic field is "n+1", then the voxel data is stored in the
384 same file as the header. In this case, the voxel data starts at offset
385 (int)vox_offset into the header file. Thus, vox_offset=352.0 means that
386 the data starts immediately after the NIFTI-1 header. If vox_offset is
387 greater than 352, the NIFTI-1 format does not say much about the
388 contents of the dataset file between the end of the header and the
389 start of the data.
390
391 FILES:
392 -----
393 If the magic field is "ni1", then the voxel data is stored in the
394 associated ".img" file, starting at offset 0 (i.e., vox_offset is not
395 used in this case, and should be set to 0.0).
396
397 When storing NIFTI-1 datasets in pairs of files, it is customary to name
398 the files in the pattern "name.hdr" and "name.img", as in ANALYZE 7.5.
399 When storing in a single file ("n+1"), the file name should be in
400 the form "name.nii" (the ".nft" and ".nif" suffixes are already taken;
401 cf. http://www.icdatamaster.com/n.html ).
402
403 BYTE ORDERING:
404 -------------
405 The byte order of the data arrays is presumed to be the same as the byte
406 order of the header (which is determined by examining dim[0]).
407
408 Floating point types are presumed to be stored in IEEE-754 format.
409 -----------------------------------------------------------------------------*/
410
411 /*---------------------------------------------------------------------------*/
412 /* DETAILS ABOUT vox_offset:
413 ------------------------
414 In a .nii file, the vox_offset field value is interpreted as the start
415 location of the image data bytes in that file. In a .hdr/.img file pair,
416 the vox_offset field value is the start location of the image data
417 bytes in the .img file.
418 * If vox_offset is less than 352 in a .nii file, it is equivalent
419 to 352 (i.e., image data never starts before byte #352 in a .nii file).
420 * The default value for vox_offset in a .nii file is 352.
421 * In a .hdr file, the default value for vox_offset is 0.
422 * vox_offset should be an integer multiple of 16; otherwise, some
423 programs may not work properly (e.g., SPM). This is to allow
424 memory-mapped input to be properly byte-aligned.
425 Note that since vox_offset is an IEEE-754 32 bit float (for compatibility
426 with the ANALYZE-7.5 format), it effectively has a 24 bit mantissa. All
427 integers from 0 to 2^24 can be represented exactly in this format, but not
428 all larger integers are exactly storable as IEEE-754 32 bit floats. However,
429 unless you plan to have vox_offset be potentially larger than 16 MB, this
430 should not be an issue. (Actually, any integral multiple of 16 up to 2^27
431 can be represented exactly in this format, which allows for up to 128 MB
432 of random information before the image data. If that isn't enough, then
433 perhaps this format isn't right for you.)
434
435 In a .img file (i.e., image data stored separately from the NIfTI-1
436 header), data bytes between #0 and #vox_offset-1 (inclusive) are completely
437 undefined and unregulated by the NIfTI-1 standard. One potential use of
438 having vox_offset > 0 in the .hdr/.img file pair storage method is to make
439 the .img file be a copy of (or link to) a pre-existing image file in some
440 other format, such as DICOM; then vox_offset would be set to the offset of
441 the image data in this file. (It may not be possible to follow the
442 "multiple-of-16 rule" with an arbitrary external file; using the NIfTI-1
443 format in such a case may lead to a file that is incompatible with software
444 that relies on vox_offset being a multiple of 16.)
445
446 In a .nii file, data bytes between #348 and #vox_offset-1 (inclusive) may
447 be used to store user-defined extra information; similarly, in a .hdr file,
448 any data bytes after byte #347 are available for user-defined extra
449 information. The (very weak) regulation of this extra header data is
450 described elsewhere.
451 -----------------------------------------------------------------------------*/
452
453 /*---------------------------------------------------------------------------*/
454 /* DATA SCALING:
455 ------------
456 If the scl_slope field is nonzero, then each voxel value in the dataset
457 should be scaled as
458 y = scl_slope * x + scl_inter
459 where x = voxel value stored
460 y = "true" voxel value
461 Normally, we would expect this scaling to be used to store "true" floating
462 values in a smaller integer datatype, but that is not required. That is,
463 it is legal to use scaling even if the datatype is a float type (crazy,
464 perhaps, but legal).
465 - However, the scaling is to be ignored if datatype is DT_RGB24.
466 - If datatype is a complex type, then the scaling is to be
467 applied to both the real and imaginary parts.
468
469 The cal_min and cal_max fields (if nonzero) are used for mapping (possibly
470 scaled) dataset values to display colors:
471 - Minimum display intensity (black) corresponds to dataset value cal_min.
472 - Maximum display intensity (white) corresponds to dataset value cal_max.
473 - Dataset values below cal_min should display as black also, and values
474 above cal_max as white.
475 - Colors "black" and "white", of course, may refer to any scalar display
476 scheme (e.g., a color lookup table specified via aux_file).
477 - cal_min and cal_max only make sense when applied to scalar-valued
478 datasets (i.e., dim[0] < 5 or dim[5] = 1).
479 -----------------------------------------------------------------------------*/
480
481 /*---------------------------------------------------------------------------*/
482 /* TYPE OF DATA (acceptable values for datatype field):
483 ---------------------------------------------------
484 Values of datatype smaller than 256 are ANALYZE 7.5 compatible.
485 Larger values are NIFTI-1 additions. These are all multiples of 256, so
486 that no bits below position 8 are set in datatype. But there is no need
487 to use only powers-of-2, as the original ANALYZE 7.5 datatype codes do.
488
489 The additional codes are intended to include a complete list of basic
490 scalar types, including signed and unsigned integers from 8 to 64 bits,
491 floats from 32 to 128 bits, and complex (float pairs) from 64 to 256 bits.
492
493 Note that most programs will support only a few of these datatypes!
494 A NIFTI-1 program should fail gracefully (e.g., print a warning message)
495 when it encounters a dataset with a type it doesn't like.
496 -----------------------------------------------------------------------------*/
497
498 #undef DT_UNKNOWN /* defined in dirent.h on some Unix systems */
499 #include<stdint.h>
500 /*! \defgroup NIFTI1_DATATYPES
501 \brief nifti1 datatype codes
502 @{
503 */
504 /*--- the original ANALYZE 7.5 type codes ---*/
505 const int32_t DT_NONE =0;
506 const int32_t DT_UNKNOWN =0; /* what it says, dude */
507 const int32_t DT_BINARY =1; /* binary (1 bit/voxel) */
508 const int32_t DT_UNSIGNED_CHAR =2; /* unsigned char (8 bits/voxel) */
509 const int32_t DT_SIGNED_SHORT =4; /* signed short (16 bits/voxel) */
510 const int32_t DT_SIGNED_INT =8; /* signed int (32 bits/voxel) */
511 const int32_t DT_FLOAT =16; /* float (32 bits/voxel) */
512 const int32_t DT_COMPLEX =32; /* complex (64 bits/voxel) */
513 const int32_t DT_DOUBLE =64; /* double (64 bits/voxel) */
514 const int32_t DT_RGB =128; /* RGB triple (24 bits/voxel) */
515 const int32_t DT_ALL =255; /* not very useful (?) */
516
517 /*----- another set of names for the same ---*/
518 const int32_t DT_UINT8 =2;
519 const int32_t DT_INT16 =4;
520 const int32_t DT_INT32 =8;
521 const int32_t DT_FLOAT32 =16;
522 const int32_t DT_COMPLEX64 =32;
523 const int32_t DT_FLOAT64 =64;
524 const int32_t DT_RGB24 =128;
525
526 /*------------------- new codes for NIFTI ---*/
527 const int32_t DT_INT8 =256; /* signed char (8 bits) */
528 const int32_t DT_UINT16 =512; /* unsigned short (16 bits) */
529 const int32_t DT_UINT32 =768; /* unsigned int (32 bits) */
530 const int32_t DT_INT64 =1024; /* long long (64 bits) */
531 const int32_t DT_UINT64 =1280; /* unsigned long long (64 bits) */
532 const int32_t DT_FLOAT128 =1536; /* long double (128 bits) */
533 const int32_t DT_COMPLEX128 =1792; /* double pair (128 bits) */
534 const int32_t DT_COMPLEX256 =2048; /* long double pair (256 bits) */
535 /* @} */
536
537
538 /*------- aliases for all the above codes ---*/
539
540 /*! \defgroup NIFTI1_DATATYPE_ALIASES
541 \brief aliases for the nifti1 datatype codes
542 @{
543 */
544 /*! unsigned char. */
545 const int32_t NIFTI_TYPE_UINT8 =2;
546 /*! signed short. */
547 const int32_t NIFTI_TYPE_INT16 =4;
548 /*! signed int. */
549 const int32_t NIFTI_TYPE_INT32 =8;
550 /*! 32 bit float. */
551 const int32_t NIFTI_TYPE_FLOAT32 =16;
552 /*! 64 bit complex = 2 32 bit floats. */
553 const int32_t NIFTI_TYPE_COMPLEX64 =32;
554 /*! 64 bit float = double. */
555 const int32_t NIFTI_TYPE_FLOAT64 =64;
556 /*! 3 8 bit bytes. */
557 const int32_t NIFTI_TYPE_RGB24 =128;
558 /*! signed char. */
559 const int32_t NIFTI_TYPE_INT8 =256;
560 /*! unsigned short. */
561 const int32_t NIFTI_TYPE_UINT16 =512;
562 /*! unsigned int. */
563 const int32_t NIFTI_TYPE_UINT32 =768;
564 /*! signed long long. */
565 const int32_t NIFTI_TYPE_INT64 =1024;
566 /*! unsigned long long. */
567 const int32_t NIFTI_TYPE_UINT64 =1280;
568 /*! 128 bit float = long double. */
569 const int32_t NIFTI_TYPE_FLOAT128 =1536;
570 /*! 128 bit complex = 2 64 bit floats. */
571 const int32_t NIFTI_TYPE_COMPLEX128 =1792;
572 /*! 256 bit complex = 2 128 bit floats */
573 const int32_t NIFTI_TYPE_COMPLEX256 =2048;
574 /* @} */
575
576 /*-------- sample typedefs for complicated types ---*/
577 #if 0
578 typedef struct { float r,i; } complex_float ;
579 typedef struct { double r,i; } complex_double ;
580 typedef struct { long double r,i; } complex_longdouble ;
581 typedef struct { unsigned char r,g,b; } rgb_byte ;
582 #endif
583
584 /*---------------------------------------------------------------------------*/
585 /* INTERPRETATION OF VOXEL DATA:
586 ----------------------------
587 The intent_code field can be used to indicate that the voxel data has
588 some particular meaning. In particular, a large number of codes is
589 given to indicate that the the voxel data should be interpreted as
590 being drawn from a given probability distribution.
591
592 VECTOR-VALUED DATASETS:
593 ----------------------
594 The 5th dimension of the dataset, if present (i.e., dim[0]=5 and
595 dim[5] > 1), contains multiple values (e.g., a vector) to be stored
596 at each spatiotemporal location. For example, the header values
597 - dim[0] = 5
598 - dim[1] = 64
599 - dim[2] = 64
600 - dim[3] = 20
601 - dim[4] = 1 (indicates no time axis)
602 - dim[5] = 3
603 - datatype = DT_FLOAT
604 - intent_code = NIFTI_INTENT_VECTOR
605 mean that this dataset should be interpreted as a 3D volume (64x64x20),
606 with a 3-vector of floats defined at each point in the 3D grid.
607
608 A program reading a dataset with a 5th dimension may want to reformat
609 the image data to store each voxels' set of values together in a struct
610 or array. This programming detail, however, is beyond the scope of the
611 NIFTI-1 file specification! Uses of dimensions 6 and 7 are also not
612 specified here.
613
614 STATISTICAL PARAMETRIC DATASETS (i.e., SPMs):
615 --------------------------------------------
616 Values of intent_code from NIFTI_FIRST_STATCODE to NIFTI_LAST_STATCODE
617 (inclusive) indicate that the numbers in the dataset should be interpreted
618 as being drawn from a given distribution. Most such distributions have
619 auxiliary parameters (e.g., NIFTI_INTENT_TTEST has 1 DOF parameter).
620
621 If the dataset DOES NOT have a 5th dimension, then the auxiliary parameters
622 are the same for each voxel, and are given in header fields intent_p1,
623 intent_p2, and intent_p3.
624
625 If the dataset DOES have a 5th dimension, then the auxiliary parameters
626 are different for each voxel. For example, the header values
627 - dim[0] = 5
628 - dim[1] = 128
629 - dim[2] = 128
630 - dim[3] = 1 (indicates a single slice)
631 - dim[4] = 1 (indicates no time axis)
632 - dim[5] = 2
633 - datatype = DT_FLOAT
634 - intent_code = NIFTI_INTENT_TTEST
635 mean that this is a 2D dataset (128x128) of t-statistics, with the
636 t-statistic being in the first "plane" of data and the degrees-of-freedom
637 parameter being in the second "plane" of data.
638
639 If the dataset 5th dimension is used to store the voxel-wise statistical
640 parameters, then dim[5] must be 1 plus the number of parameters required
641 by that distribution (e.g., intent_code=NIFTI_INTENT_TTEST implies dim[5]
642 must be 2, as in the example just above).
643
644 Note: intent_code values 2..10 are compatible with AFNI 1.5x (which is
645 why there is no code with value=1, which is obsolescent in AFNI).
646
647 OTHER INTENTIONS:
648 ----------------
649 The purpose of the intent_* fields is to help interpret the values
650 stored in the dataset. Some non-statistical values for intent_code
651 and conventions are provided for storing other complex data types.
652
653 The intent_name field provides space for a 15 character (plus 0 byte)
654 'name' string for the type of data stored. Examples:
655 - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1";
656 could be used to signify that the voxel values are estimates of the
657 NMR parameter T1.
658 - intent_code = NIFTI_INTENT_TTEST; intent_name = "House";
659 could be used to signify that the voxel values are t-statistics
660 for the significance of 'activation' response to a House stimulus.
661 - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152";
662 could be used to signify that the voxel values are a displacement
663 vector that transforms each voxel (x,y,z) location to the
664 corresponding location in the MNI152 standard brain.
665 - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI";
666 could be used to signify that the voxel values comprise a diffusion
667 tensor image.
668
669 If no data name is implied or needed, intent_name[0] should be set to 0.
670 -----------------------------------------------------------------------------*/
671
672 /*! default: no intention is indicated in the header. */
673
674 const int32_t NIFTI_INTENT_NONE =0;
675
676 /*-------- These codes are for probability distributions ---------------*/
677 /* Most distributions have a number of parameters,
678 below denoted by p1, p2, and p3, and stored in
679 - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension
680 - image data array if dataset does have 5th dimension
681
682 Functions to compute with many of the distributions below can be found
683 in the CDF library from U Texas.
684
685 Formulas for and discussions of these distributions can be found in the
686 following books:
687
688 [U] Univariate Discrete Distributions,
689 NL Johnson, S Kotz, AW Kemp.
690
691 [C1] Continuous Univariate Distributions, vol. 1,
692 NL Johnson, S Kotz, N Balakrishnan.
693
694 [C2] Continuous Univariate Distributions, vol. 2,
695 NL Johnson, S Kotz, N Balakrishnan. */
696 /*----------------------------------------------------------------------*/
697
698 /*! [C2, chap 32] Correlation coefficient R (1 param):
699 p1 = degrees of freedom
700 R/sqrt(1-R*R) is t-distributed with p1 DOF. */
701
702 /*! \defgroup NIFTI1_INTENT_CODES
703 \brief nifti1 intent codes, to describe intended meaning of dataset contents
704 @{
705 */
706 const int32_t NIFTI_INTENT_CORREL =2;
707
708 /*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */
709
710 const int32_t NIFTI_INTENT_TTEST =3;
711
712 /*! [C2, chap 27] Fisher F statistic (2 params):
713 p1 = numerator DOF, p2 = denominator DOF. */
714
715 const int32_t NIFTI_INTENT_FTEST =4;
716
717 /*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */
718
719 const int32_t NIFTI_INTENT_ZSCORE =5;
720
721 /*! [C1, chap 18] Chi-squared (1 param): p1 = DOF.
722 Density(x) proportional to exp(-x/2) * x^(p1/2-1). */
723
724 const int32_t NIFTI_INTENT_CHISQ =6;
725
726 /*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b.
727 Density(x) proportional to x^(a-1) * (1-x)^(b-1). */
728
729 const int32_t NIFTI_INTENT_BETA =7;
730
731 /*! [U, chap 3] Binomial distribution (2 params):
732 p1 = number of trials, p2 = probability per trial.
733 Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */
734
735 const int32_t NIFTI_INTENT_BINOM =8;
736
737 /*! [C1, chap 17] Gamma distribution (2 params):
738 p1 = shape, p2 = scale.
739 Density(x) proportional to x^(p1-1) * exp(-p2*x). */
740
741 const int32_t NIFTI_INTENT_GAMMA =9;
742
743 /*! [U, chap 4] Poisson distribution (1 param): p1 = mean.
744 Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */
745
746 const int32_t NIFTI_INTENT_POISSON =10;
747
748 /*! [C1, chap 13] Normal distribution (2 params):
749 p1 = mean, p2 = standard deviation. */
750
751 const int32_t NIFTI_INTENT_NORMAL =11;
752
753 /*! [C2, chap 30] Noncentral F statistic (3 params):
754 p1 = numerator DOF, p2 = denominator DOF,
755 p3 = numerator noncentrality parameter. */
756
757 const int32_t NIFTI_INTENT_FTEST_NONC=12;
758
759 /*! [C2, chap 29] Noncentral chi-squared statistic (2 params):
760 p1 = DOF, p2 = noncentrality parameter. */
761
762 const int32_t NIFTI_INTENT_CHISQ_NONC=13;
763
764 /*! [C2, chap 23] Logistic distribution (2 params):
765 p1 = location, p2 = scale.
766 Density(x) proportional to sech^2((x-p1)/(2*p2)). */
767
768 const int32_t NIFTI_INTENT_LOGISTIC =14;
769
770 /*! [C2, chap 24] Laplace distribution (2 params):
771 p1 = location, p2 = scale.
772 Density(x) proportional to exp(-abs(x-p1)/p2). */
773
774 const int32_t NIFTI_INTENT_LAPLACE =15;
775
776 /*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */
777
778 const int32_t NIFTI_INTENT_UNIFORM =16;
779
780 /*! [C2, chap 31] Noncentral t statistic (2 params):
781 p1 = DOF, p2 = noncentrality parameter. */
782
783 const int32_t NIFTI_INTENT_TTEST_NONC=17;
784
785 /*! [C1, chap 21] Weibull distribution (3 params):
786 p1 = location, p2 = scale, p3 = power.
787 Density(x) proportional to
788 ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */
789
790 const int32_t NIFTI_INTENT_WEIBULL =18;
791
792 /*! [C1, chap 18] Chi distribution (1 param): p1 = DOF.
793 Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0.
794 p1 = 1 = 'half normal' distribution
795 p1 = 2 = Rayleigh distribution
796 p1 = 3 = Maxwell-Boltzmann distribution. */
797
798 const int32_t NIFTI_INTENT_CHI =19;
799
800 /*! [C1, chap 15] Inverse Gaussian (2 params):
801 p1 = mu, p2 = lambda
802 Density(x) proportional to
803 exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3 for x > 0. */
804
805 const int32_t NIFTI_INTENT_INVGAUSS =20;
806
807 /*! [C2, chap 22] Extreme value type I (2 params):
808 p1 = location, p2 = scale
809 cdf(x) = exp(-exp(-(x-p1)/p2)). */
810
811 const int32_t NIFTI_INTENT_EXTVAL =21;
812
813 /*! Data is a 'p-value' (no params). */
814
815 const int32_t NIFTI_INTENT_PVAL =22;
816
817 /*! Data is ln(p-value) (no params).
818 To be safe, a program should compute p = exp(-abs(this_value)).
819 The nifti_stats.c library returns this_value
820 as positive, so that this_value = -log(p). */
821
822
823 const int32_t NIFTI_INTENT_LOGPVAL =23;
824
825 /*! Data is log10(p-value) (no params).
826 To be safe, a program should compute p = pow(10.,-abs(this_value)).
827 The nifti_stats.c library returns this_value
828 as positive, so that this_value = -log10(p). */
829
830 const int32_t NIFTI_INTENT_LOG10PVAL =24;
831
832 /*! Smallest intent_code that indicates a statistic. */
833
834 const int32_t NIFTI_FIRST_STATCODE =2;
835
836 /*! Largest intent_code that indicates a statistic. */
837
838 const int32_t NIFTI_LAST_STATCODE =24;
839
840 /*---------- these values for intent_code aren't for statistics ----------*/
841
842 /*! To signify that the value at each voxel is an estimate
843 of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE.
844 The name of the parameter may be stored in intent_name. */
845
846 const int32_t NIFTI_INTENT_ESTIMATE =1001;
847
848 /*! To signify that the value at each voxel is an index into
849 some set of labels, set intent_code = NIFTI_INTENT_LABEL.
850 The filename with the labels may stored in aux_file. */
851
852 const int32_t NIFTI_INTENT_LABEL =1002;
853
854 /*! To signify that the value at each voxel is an index into the
855 NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */
856
857 const int32_t NIFTI_INTENT_NEURONAME=1003;
858
859 /*! To store an M x N matrix at each voxel:
860 - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1)
861 - intent_code must be NIFTI_INTENT_GENMATRIX
862 - dim[5] must be M*N
863 - intent_p1 must be M (in float format)
864 - intent_p2 must be N (ditto)
865 - the matrix values A[i][[j] are stored in row-order:
866 - A[0][0] A[0][1] ... A[0][N-1]
867 - A[1][0] A[1][1] ... A[1][N-1]
868 - etc., until
869 - A[M-1][0] A[M-1][1] ... A[M-1][N-1] */
870
871 const int32_t NIFTI_INTENT_GENMATRIX=1004;
872
873 /*! To store an NxN symmetric matrix at each voxel:
874 - dataset must have a 5th dimension
875 - intent_code must be NIFTI_INTENT_SYMMATRIX
876 - dim[5] must be N*(N+1)/2
877 - intent_p1 must be N (in float format)
878 - the matrix values A[i][[j] are stored in row-order:
879 - A[0][0]
880 - A[1][0] A[1][1]
881 - A[2][0] A[2][1] A[2][2]
882 - etc.: row-by-row */
883
884 const int32_t NIFTI_INTENT_SYMMATRIX=1005;
885
886 /*! To signify that the vector value at each voxel is to be taken
887 as a displacement field or vector:
888 - dataset must have a 5th dimension
889 - intent_code must be NIFTI_INTENT_DISPVECT
890 - dim[5] must be the dimensionality of the displacment
891 vector (e.g., 3 for spatial displacement, 2 for in-plane) */
892
893 const int32_t NIFTI_INTENT_DISPVECT =1006; /* specifically for displacements */
894 const int32_t NIFTI_INTENT_VECTOR =1007; /* for any other type of vector */
895
896 /*! To signify that the vector value at each voxel is really a
897 spatial coordinate (e.g., the vertices or nodes of a surface mesh):
898 - dataset must have a 5th dimension
899 - intent_code must be NIFTI_INTENT_POINTSET
900 - dim[0] = 5
901 - dim[1] = number of points
902 - dim[2] = dim[3] = dim[4] = 1
903 - dim[5] must be the dimensionality of space (e.g., 3 => 3D space).
904 - intent_name may describe the object these points come from
905 (e.g., "pial", "gray/white" , "EEG", "MEG"). */
906
907 const int32_t NIFTI_INTENT_POINTSET =1008;
908
909 /*! To signify that the vector value at each voxel is really a triple
910 of indexes (e.g., forming a triangle) from a pointset dataset:
911 - dataset must have a 5th dimension
912 - intent_code must be NIFTI_INTENT_TRIANGLE
913 - dim[0] = 5
914 - dim[1] = number of triangles
915 - dim[2] = dim[3] = dim[4] = 1
916 - dim[5] = 3
917 - datatype should be an integer type (preferably DT_INT32)
918 - the data values are indexes (0,1,...) into a pointset dataset. */
919
920 const int32_t NIFTI_INTENT_TRIANGLE =1009;
921
922 /*! To signify that the vector value at each voxel is a quaternion:
923 - dataset must have a 5th dimension
924 - intent_code must be NIFTI_INTENT_QUATERNION
925 - dim[0] = 5
926 - dim[5] = 4
927 - datatype should be a floating point type */
928
929 const int32_t NIFTI_INTENT_QUATERNION=1010;
930
931 /*! Dimensionless value - no params - although, as in _ESTIMATE
932 the name of the parameter may be stored in intent_name. */
933
934 const int32_t NIFTI_INTENT_DIMLESS =1011;
935 /* @} */
936
937 /*---------------------------------------------------------------------------*/
938 /* 3D IMAGE (VOLUME) ORIENTATION AND LOCATION IN SPACE:
939 ---------------------------------------------------
940 There are 3 different methods by which continuous coordinates can
941 attached to voxels. The discussion below emphasizes 3D volumes, and
942 the continuous coordinates are referred to as (x,y,z). The voxel
943 index coordinates (i.e., the array indexes) are referred to as (i,j,k),
944 with valid ranges:
945 i = 0 .. dim[1]-1
946 j = 0 .. dim[2]-1 (if dim[0] >= 2)
947 k = 0 .. dim[3]-1 (if dim[0] >= 3)
948 The (x,y,z) coordinates refer to the CENTER of a voxel. In methods
949 2 and 3, the (x,y,z) axes refer to a subject-based coordinate system,
950 with
951 +x = Right +y = Anterior +z = Superior.
952 This is a right-handed coordinate system. However, the exact direction
953 these axes point with respect to the subject depends on qform_code
954 (Method 2) and sform_code (Method 3).
955
956 N.B.: The i index varies most rapidly, j index next, k index slowest.
957 Thus, voxel (i,j,k) is stored starting at location
958 (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8)
959 into the dataset array.
960
961 N.B.: The ANALYZE 7.5 coordinate system is
962 +x = Left +y = Anterior +z = Superior
963 which is a left-handed coordinate system. This backwardness is
964 too difficult to tolerate, so this NIFTI-1 standard specifies the
965 coordinate order which is most common in functional neuroimaging.
966
967 N.B.: The 3 methods below all give the locations of the voxel centers
968 in the (x,y,z) coordinate system. In many cases, programs will wish
969 to display image data on some other grid. In such a case, the program
970 will need to convert its desired (x,y,z) values into (i,j,k) values
971 in order to extract (or interpolate) the image data. This operation
972 would be done with the inverse transformation to those described below.
973
974 N.B.: Method 2 uses a factor 'qfac' which is either -1 or 1; qfac is
975 stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which
976 should not occur), we take qfac=1. Of course, pixdim[0] is only used
977 when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header.
978
979 N.B.: The units of (x,y,z) can be specified using the xyzt_units field.
980
981 METHOD 1 (the "old" way, used only when qform_code = 0):
982 -------------------------------------------------------
983 The coordinate mapping from (i,j,k) to (x,y,z) is the ANALYZE
984 7.5 way. This is a simple scaling relationship:
985
986 x = pixdim[1] * i
987 y = pixdim[2] * j
988 z = pixdim[3] * k
989
990 No particular spatial orientation is attached to these (x,y,z)
991 coordinates. (NIFTI-1 does not have the ANALYZE 7.5 orient field,
992 which is not general and is often not set properly.) This method
993 is not recommended, and is present mainly for compatibility with
994 ANALYZE 7.5 files.
995
996 METHOD 2 (used when qform_code > 0, which should be the "normal" case):
997 ---------------------------------------------------------------------
998 The (x,y,z) coordinates are given by the pixdim[] scales, a rotation
999 matrix, and a shift. This method is intended to represent
1000 "scanner-anatomical" coordinates, which are often embedded in the
1001 image header (e.g., DICOM fields (0020,0032), (0020,0037), (0028,0030),
1002 and (0018,0050)), and represent the nominal orientation and location of
1003 the data. This method can also be used to represent "aligned"
1004 coordinates, which would typically result from some post-acquisition
1005 alignment of the volume to a standard orientation (e.g., the same
1006 subject on another day, or a rigid rotation to true anatomical
1007 orientation from the tilted position of the subject in the scanner).
1008 The formula for (x,y,z) in terms of header parameters and (i,j,k) is:
1009
1010 [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ]
1011 [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ]
1012 [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ]
1013
1014 The qoffset_* shifts are in the NIFTI-1 header. Note that the center
1015 of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is
1016 just (x,y,z)=(qoffset_x,qoffset_y,qoffset_z).
1017
1018 The rotation matrix R is calculated from the quatern_* parameters.
1019 This calculation is described below.
1020
1021 The scaling factor qfac is either 1 or -1. The rotation matrix R
1022 defined by the quaternion parameters is "proper" (has determinant 1).
1023 This may not fit the needs of the data; for example, if the image
1024 grid is
1025 i increases from Left-to-Right
1026 j increases from Anterior-to-Posterior
1027 k increases from Inferior-to-Superior
1028 Then (i,j,k) is a left-handed triple. In this example, if qfac=1,
1029 the R matrix would have to be
1030
1031 [ 1 0 0 ]
1032 [ 0 -1 0 ] which is "improper" (determinant = -1).
1033 [ 0 0 1 ]
1034
1035 If we set qfac=-1, then the R matrix would be
1036
1037 [ 1 0 0 ]
1038 [ 0 -1 0 ] which is proper.
1039 [ 0 0 -1 ]
1040
1041 This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0]
1042 (which encodes a 180 degree rotation about the x-axis).
1043
1044 METHOD 3 (used when sform_code > 0):
1045 -----------------------------------
1046 The (x,y,z) coordinates are given by a general affine transformation
1047 of the (i,j,k) indexes:
1048
1049 x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3]
1050 y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3]
1051 z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3]
1052
1053 The srow_* vectors are in the NIFTI_1 header. Note that no use is
1054 made of pixdim[] in this method.
1055
1056 WHY 3 METHODS?
1057 --------------
1058 Method 1 is provided only for backwards compatibility. The intention
1059 is that Method 2 (qform_code > 0) represents the nominal voxel locations
1060 as reported by the scanner, or as rotated to some fiducial orientation and
1061 location. Method 3, if present (sform_code > 0), is to be used to give
1062 the location of the voxels in some standard space. The sform_code
1063 indicates which standard space is present. Both methods 2 and 3 can be
1064 present, and be useful in different contexts (method 2 for displaying the
1065 data on its original grid; method 3 for displaying it on a standard grid).
1066
1067 In this scheme, a dataset would originally be set up so that the
1068 Method 2 coordinates represent what the scanner reported. Later,
1069 a registration to some standard space can be computed and inserted
1070 in the header. Image display software can use either transform,
1071 depending on its purposes and needs.
1072
1073 In Method 2, the origin of coordinates would generally be whatever
1074 the scanner origin is; for example, in MRI, (0,0,0) is the center
1075 of the gradient coil.
1076
1077 In Method 3, the origin of coordinates would depend on the value
1078 of sform_code; for example, for the Talairach coordinate system,
1079 (0,0,0) corresponds to the Anterior Commissure.
1080
1081 QUATERNION REPRESENTATION OF ROTATION MATRIX (METHOD 2)
1082 -------------------------------------------------------
1083 The orientation of the (x,y,z) axes relative to the (i,j,k) axes
1084 in 3D space is specified using a unit quaternion [a,b,c,d], where
1085 a*a+b*b+c*c+d*d=1. The (b,c,d) values are all that is needed, since
1086 we require that a = sqrt(1.0-(b*b+c*c+d*d)) be nonnegative. The (b,c,d)
1087 values are stored in the (quatern_b,quatern_c,quatern_d) fields.
1088
1089 The quaternion representation is chosen for its compactness in
1090 representing rotations. The (proper) 3x3 rotation matrix that
1091 corresponds to [a,b,c,d] is
1092
1093 [ a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c ]
1094 R = [ 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b ]
1095 [ 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b ]
1096
1097 [ R11 R12 R13 ]
1098 = [ R21 R22 R23 ]
1099 [ R31 R32 R33 ]
1100
1101 If (p,q,r) is a unit 3-vector, then rotation of angle h about that
1102 direction is represented by the quaternion
1103
1104 [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)].
1105
1106 Requiring a >= 0 is equivalent to requiring -Pi <= h <= Pi. (Note that
1107 [-a,-b,-c,-d] represents the same rotation as [a,b,c,d]; there are 2
1108 quaternions that can be used to represent a given rotation matrix R.)
1109 To rotate a 3-vector (x,y,z) using quaternions, we compute the
1110 quaternion product
1111
1112 [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d]
1113
1114 which is equivalent to the matrix-vector multiply
1115
1116 [ x' ] [ x ]
1117 [ y' ] = R [ y ] (equivalence depends on a*a+b*b+c*c+d*d=1)
1118 [ z' ] [ z ]
1119
1120 Multiplication of 2 quaternions is defined by the following:
1121
1122 [a,b,c,d] = a*1 + b*I + c*J + d*K
1123 where
1124 I*I = J*J = K*K = -1 (I,J,K are square roots of -1)
1125 I*J = K J*K = I K*I = J
1126 J*I = -K K*J = -I I*K = -J (not commutative!)
1127 For example
1128 [a,b,0,0] * [0,0,0,1] = [0,0,-b,a]
1129 since this expands to
1130 (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J).
1131
1132 The above formula shows how to go from quaternion (b,c,d) to
1133 rotation matrix and direction cosines. Conversely, given R,
1134 we can compute the fields for the NIFTI-1 header by
1135
1136 a = 0.5 * sqrt(1+R11+R22+R33) (not stored)
1137 b = 0.25 * (R32-R23) / a => quatern_b
1138 c = 0.25 * (R13-R31) / a => quatern_c
1139 d = 0.25 * (R21-R12) / a => quatern_d
1140
1141 If a=0 (a 180 degree rotation), alternative formulas are needed.
1142 See the nifti1_io.c function mat44_to_quatern() for an implementation
1143 of the various cases in converting R to [a,b,c,d].
1144
1145 Note that R-transpose (= R-inverse) would lead to the quaternion
1146 [a,-b,-c,-d].
1147
1148 The choice to specify the qoffset_x (etc.) values in the final
1149 coordinate system is partly to make it easy to convert DICOM images to
1150 this format. The DICOM attribute "Image Position (Patient)" (0020,0032)
1151 stores the (Xd,Yd,Zd) coordinates of the center of the first voxel.
1152 Here, (Xd,Yd,Zd) refer to DICOM coordinates, and Xd=-x, Yd=-y, Zd=z,
1153 where (x,y,z) refers to the NIFTI coordinate system discussed above.
1154 (i.e., DICOM +Xd is Left, +Yd is Posterior, +Zd is Superior,
1155 whereas +x is Right, +y is Anterior , +z is Superior. )
1156 Thus, if the (0020,0032) DICOM attribute is extracted into (px,py,pz), then
1157 qoffset_x = -px qoffset_y = -py qoffset_z = pz
1158 is a reasonable setting when qform_code=NIFTI_XFORM_SCANNER_ANAT.
1159
1160 That is, DICOM's coordinate system is 180 degrees rotated about the z-axis
1161 from the neuroscience/NIFTI coordinate system. To transform between DICOM
1162 and NIFTI, you just have to negate the x- and y-coordinates.
1163
1164 The DICOM attribute (0020,0037) "Image Orientation (Patient)" gives the
1165 orientation of the x- and y-axes of the image data in terms of 2 3-vectors.
1166 The first vector is a unit vector along the x-axis, and the second is
1167 along the y-axis. If the (0020,0037) attribute is extracted into the
1168 value (xa,xb,xc,ya,yb,yc), then the first two columns of the R matrix
1169 would be
1170 [ -xa -ya ]
1171 [ -xb -yb ]
1172 [ xc yc ]
1173 The negations are because DICOM's x- and y-axes are reversed relative
1174 to NIFTI's. The third column of the R matrix gives the direction of
1175 displacement (relative to the subject) along the slice-wise direction.
1176 This orientation is not encoded in the DICOM standard in a simple way;
1177 DICOM is mostly concerned with 2D images. The third column of R will be
1178 either the cross-product of the first 2 columns or its negative. It is
1179 possible to infer the sign of the 3rd column by examining the coordinates
1180 in DICOM attribute (0020,0032) "Image Position (Patient)" for successive
1181 slices. However, this method occasionally fails for reasons that I
1182 (RW Cox) do not understand.
1183 -----------------------------------------------------------------------------*/
1184
1185 /* [qs]form_code value: */ /* x,y,z coordinate system refers to: */
1186 /*-----------------------*/ /*---------------------------------------*/
1187
1188 /*! \defgroup NIFTI1_XFORM_CODES
1189 \brief nifti1 xform codes to describe the "standard" coordinate system
1190 @{
1191 */
1192 /*! Arbitrary coordinates (Method 1). */
1193
1194 const int32_t NIFTI_XFORM_UNKNOWN =0;
1195
1196 /*! Scanner-based anatomical coordinates */
1197
1198 const int32_t NIFTI_XFORM_SCANNER_ANAT=1;
1199
1200 /*! Coordinates aligned to another file's,
1201 or to anatomical "truth". */
1202
1203 const int32_t NIFTI_XFORM_ALIGNED_ANAT=2;
1204
1205 /*! Coordinates aligned to Talairach-
1206 Tournoux Atlas; (0,0,0)=AC, etc. */
1207
1208 const int32_t NIFTI_XFORM_TALAIRACH =3;
1209
1210 /*! MNI 152 normalized coordinates. */
1211
1212 const int32_t NIFTI_XFORM_MNI_152 =4;
1213 /* @} */
1214
1215 /*---------------------------------------------------------------------------*/
1216 /* UNITS OF SPATIAL AND TEMPORAL DIMENSIONS:
1217 ----------------------------------------
1218 The codes below can be used in xyzt_units to indicate the units of pixdim.
1219 As noted earlier, dimensions 1,2,3 are for x,y,z; dimension 4 is for
1220 time (t).
1221 - If dim[4]=1 or dim[0] < 4, there is no time axis.
1222 - A single time series (no space) would be specified with
1223 - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data)
1224 - dim[1] = dim[2] = dim[3] = 1
1225 - dim[4] = number of time points
1226 - pixdim[4] = time step
1227 - xyzt_units indicates units of pixdim[4]
1228 - dim[5] = number of values stored at each time point
1229
1230 Bits 0..2 of xyzt_units specify the units of pixdim[1..3]
1231 (e.g., spatial units are values 1..7).
1232 Bits 3..5 of xyzt_units specify the units of pixdim[4]
1233 (e.g., temporal units are multiples of 8).
1234
1235 This compression of 2 distinct concepts into 1 byte is due to the
1236 limited space available in the 348 byte ANALYZE 7.5 header. The
1237 macros XYZT_TO_SPACE and XYZT_TO_TIME can be used to mask off the
1238 undesired bits from the xyzt_units fields, leaving "pure" space
1239 and time codes. Inversely, the macro SPACE_TIME_TO_XYZT can be
1240 used to assemble a space code (0,1,2,...,7) with a time code
1241 (0,8,16,32,...,56) into the combined value for xyzt_units.
1242
1243 Note that codes are provided to indicate the "time" axis units are
1244 actually frequency in Hertz (_HZ), in part-per-million (_PPM)
1245 or in radians-per-second (_RADS).
1246
1247 The toffset field can be used to indicate a nonzero start point for
1248 the time axis. That is, time point #m is at t=toffset+m*pixdim[4]
1249 for m=0..dim[4]-1.
1250 -----------------------------------------------------------------------------*/
1251
1252 /*! \defgroup NIFTI1_UNITS
1253 \brief nifti1 units codes to describe the unit of measurement for
1254 each dimension of the dataset
1255 @{
1256 */
1257 /*! NIFTI code for unspecified units. */
1258 const int32_t NIFTI_UNITS_UNKNOWN=0;
1259
1260 /** Space codes are multiples of 1. **/
1261 /*! NIFTI code for meters. */
1262 const int32_t NIFTI_UNITS_METER =1;
1263 /*! NIFTI code for millimeters. */
1264 const int32_t NIFTI_UNITS_MM =2;
1265 /*! NIFTI code for micrometers. */
1266 const int32_t NIFTI_UNITS_MICRON =3;
1267
1268 /** Time codes are multiples of 8. **/
1269 /*! NIFTI code for seconds. */
1270 const int32_t NIFTI_UNITS_SEC =8;
1271 /*! NIFTI code for milliseconds. */
1272 const int32_t NIFTI_UNITS_MSEC =16;
1273 /*! NIFTI code for microseconds. */
1274 const int32_t NIFTI_UNITS_USEC =24;
1275
1276 /*** These units are for spectral data: ***/
1277 /*! NIFTI code for Hertz. */
1278 const int32_t NIFTI_UNITS_HZ =32;
1279 /*! NIFTI code for ppm. */
1280 const int32_t NIFTI_UNITS_PPM =40;
1281 /*! NIFTI code for radians per second. */
1282 const int32_t NIFTI_UNITS_RADS =48;
1283 /* @} */
1284
1285 #undef XYZT_TO_SPACE
1286 #undef XYZT_TO_TIME
1287 #define XYZT_TO_SPACE(xyzt) ( (xyzt) & 0x07 )
1288 #define XYZT_TO_TIME(xyzt) ( (xyzt) & 0x38 )
1289
1290 #undef SPACE_TIME_TO_XYZT
1291 #define SPACE_TIME_TO_XYZT(ss,tt) ( (((char)(ss)) & 0x07) \
1292 | (((char)(tt)) & 0x38) )
1293
1294 /*---------------------------------------------------------------------------*/
1295 /* MRI-SPECIFIC SPATIAL AND TEMPORAL INFORMATION:
1296 ---------------------------------------------
1297 A few fields are provided to store some extra information
1298 that is sometimes important when storing the image data
1299 from an FMRI time series experiment. (After processing such
1300 data into statistical images, these fields are not likely
1301 to be useful.)
1302
1303 { freq_dim } = These fields encode which spatial dimension (1,2, or 3)
1304 { phase_dim } = corresponds to which acquisition dimension for MRI data.
1305 { slice_dim } =
1306 Examples:
1307 Rectangular scan multi-slice EPI:
1308 freq_dim = 1 phase_dim = 2 slice_dim = 3 (or some permutation)
1309 Spiral scan multi-slice EPI:
1310 freq_dim = phase_dim = 0 slice_dim = 3
1311 since the concepts of frequency- and phase-encoding directions
1312 don't apply to spiral scan
1313
1314 slice_duration = If this is positive, AND if slice_dim is nonzero,
1315 indicates the amount of time used to acquire 1 slice.
1316 slice_duration*dim[slice_dim] can be less than pixdim[4]
1317 with a clustered acquisition method, for example.
1318
1319 slice_code = If this is nonzero, AND if slice_dim is nonzero, AND
1320 if slice_duration is positive, indicates the timing
1321 pattern of the slice acquisition. The following codes
1322 are defined:
1323 NIFTI_SLICE_SEQ_INC == sequential increasing
1324 NIFTI_SLICE_SEQ_DEC == sequential decreasing
1325 NIFTI_SLICE_ALT_INC == alternating increasing
1326 NIFTI_SLICE_ALT_DEC == alternating decreasing
1327 NIFTI_SLICE_ALT_INC2 == alternating increasing #2
1328 NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2
1329 { slice_start } = Indicates the start and end of the slice acquisition
1330 { slice_end } = pattern, when slice_code is nonzero. These values
1331 are present to allow for the possible addition of
1332 "padded" slices at either end of the volume, which
1333 don't fit into the slice timing pattern. If there
1334 are no padding slices, then slice_start=0 and
1335 slice_end=dim[slice_dim]-1 are the correct values.
1336 For these values to be meaningful, slice_start must
1337 be non-negative and slice_end must be greater than
1338 slice_start. Otherwise, they should be ignored.
1339
1340 The following table indicates the slice timing pattern, relative to
1341 time=0 for the first slice acquired, for some sample cases. Here,
1342 dim[slice_dim]=7 (there are 7 slices, labeled 0..6), slice_duration=0.1,
1343 and slice_start=1, slice_end=5 (1 padded slice on each end).
1344
1345 slice
1346 index SEQ_INC SEQ_DEC ALT_INC ALT_DEC ALT_INC2 ALT_DEC2
1347 6 : n/a n/a n/a n/a n/a n/a n/a = not applicable
1348 5 : 0.4 0.0 0.2 0.0 0.4 0.2 (slice time offset
1349 4 : 0.3 0.1 0.4 0.3 0.1 0.0 doesn't apply to
1350 3 : 0.2 0.2 0.1 0.1 0.3 0.3 slices outside
1351 2 : 0.1 0.3 0.3 0.4 0.0 0.1 the range
1352 1 : 0.0 0.4 0.0 0.2 0.2 0.4 slice_start ..
1353 0 : n/a n/a n/a n/a n/a n/a slice_end)
1354
1355 The SEQ slice_codes are sequential ordering (uncommon but not unknown),
1356 either increasing in slice number or decreasing (INC or DEC), as
1357 illustrated above.
1358
1359 The ALT slice codes are alternating ordering. The 'standard' way for
1360 these to operate (without the '2' on the end) is for the slice timing
1361 to start at the edge of the slice_start .. slice_end group (at slice_start
1362 for INC and at slice_end for DEC). For the 'ALT_*2' slice_codes, the
1363 slice timing instead starts at the first slice in from the edge (at
1364 slice_start+1 for INC2 and at slice_end-1 for DEC2). This latter
1365 acquisition scheme is found on some Siemens scanners.
1366
1367 The fields freq_dim, phase_dim, slice_dim are all squished into the single
1368 byte field dim_info (2 bits each, since the values for each field are
1369 limited to the range 0..3). This unpleasantness is due to lack of space
1370 in the 348 byte allowance.
1371
1372 The macros DIM_INFO_TO_FREQ_DIM, DIM_INFO_TO_PHASE_DIM, and
1373 DIM_INFO_TO_SLICE_DIM can be used to extract these values from the
1374 dim_info byte.
1375
1376 The macro FPS_INTO_DIM_INFO can be used to put these 3 values
1377 into the dim_info byte.
1378 -----------------------------------------------------------------------------*/
1379
1380 #undef DIM_INFO_TO_FREQ_DIM
1381 #undef DIM_INFO_TO_PHASE_DIM
1382 #undef DIM_INFO_TO_SLICE_DIM
1383
1384 #define DIM_INFO_TO_FREQ_DIM(di) ( ((di) ) & 0x03 )
1385 #define DIM_INFO_TO_PHASE_DIM(di) ( ((di) >> 2) & 0x03 )
1386 #define DIM_INFO_TO_SLICE_DIM(di) ( ((di) >> 4) & 0x03 )
1387
1388 #undef FPS_INTO_DIM_INFO
1389 #define FPS_INTO_DIM_INFO(fd,pd,sd) ( ( ( ((char)(fd)) & 0x03) ) | \
1390 ( ( ((char)(pd)) & 0x03) << 2 ) | \
1391 ( ( ((char)(sd)) & 0x03) << 4 ) )
1392
1393 /*! \defgroup NIFTI1_SLICE_ORDER
1394 \brief nifti1 slice order codes, describing the acquisition order
1395 of the slices
1396 @{
1397 */
1398 const int32_t NIFTI_SLICE_UNKNOWN =0;
1399 const int32_t NIFTI_SLICE_SEQ_INC =1;
1400 const int32_t NIFTI_SLICE_SEQ_DEC =2;
1401 const int32_t NIFTI_SLICE_ALT_INC =3;
1402 const int32_t NIFTI_SLICE_ALT_DEC =4;
1403 const int32_t NIFTI_SLICE_ALT_INC2 =5; /* 05 May 2005: RWCox */
1404 const int32_t NIFTI_SLICE_ALT_DEC2 =6; /* 05 May 2005: RWCox */
1405 /* @} */
1406
1407 /*---------------------------------------------------------------------------*/
1408 /* UNUSED FIELDS:
1409 -------------
1410 Some of the ANALYZE 7.5 fields marked as ++UNUSED++ may need to be set
1411 to particular values for compatibility with other programs. The issue
1412 of interoperability of ANALYZE 7.5 files is a murky one -- not all
1413 programs require exactly the same set of fields. (Unobscuring this
1414 murkiness is a principal motivation behind NIFTI-1.)
1415
1416 Some of the fields that may need to be set for other (non-NIFTI aware)
1417 software to be happy are:
1418
1419 extents dbh.h says this should be 16384
1420 regular dbh.h says this should be the character 'r'
1421 glmin, } dbh.h says these values should be the min and max voxel
1422 glmax } values for the entire dataset
1423
1424 It is best to initialize ALL fields in the NIFTI-1 header to 0
1425 (e.g., with calloc()), then fill in what is needed.
1426 -----------------------------------------------------------------------------*/
1427
1428 /*---------------------------------------------------------------------------*/
1429 /* MISCELLANEOUS C MACROS
1430 -----------------------------------------------------------------------------*/
1431
1432 /*.................*/
1433 /*! Given a nifti_1_header struct, check if it has a good magic number.
1434 Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */
1435
1436 /*#define NIFTI_VERSION(h) \
1437 ( ( (h).magic[0]=='n' && (h).magic[3]=='\0' && \
1438 ( (h).magic[1]=='i' || (h).magic[1]=='+' ) && \
1439 ( (h).magic[2]>='1' && (h).magic[2]<='9' ) ) \
1440 ? (h).magic[2]-'0' : 0 )
1441
1442 //*/
1443
1444 /*.................*/
1445 /*! Check if a nifti_1_header struct says if the data is stored in the
1446 same file or in a separate file. Returns 1 if the data is in the same
1447 file as the header, 0 if it is not. */
1448
1449 //#define NIFTI_ONEFILE(h) ( (h).magic[1] == '+' )
1450
1451 /*.................*/
1452 /*! Check if a nifti_1_header struct needs to be byte swapped.
1453 Returns 1 if it needs to be swapped, 0 if it does not. */
1454
1455 //#define NIFTI_NEEDS_SWAP(h) ( (h).dim[0] < 0 || (h).dim[0] > 7 )
1456
1457 /*.................*/
1458 /*! Check if a nifti_1_header struct contains a 5th (vector) dimension.
1459 Returns size of 5th dimension if > 1, returns 0 otherwise. */
1460
1461 //#define NIFTI_5TH_DIM(h) ( ((h).dim[0]>4 && (h).dim[5]>1) ? (h).dim[5] : 0 )
1462
1463 /*****************************************************************************/
1464
1465 }//namespace
1466
1467 #endif /* _NIFTI_HEADER_ */
0 #ifndef __NIFTI2_HEADER
1 #define __NIFTI2_HEADER
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "nifti1.h"
31
32 namespace cifti
33 {
34
35 #include <stdint.h>
36 /*extended nifti intent codes*/
37 const int32_t NIFTI_INTENT_CONNECTIVITY_UNKNOWN=3000;
38 const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE=3001;
39 const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_TIME=3002;//CIFTI-1 name
40 const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_SERIES=3002;//CIFTI-2 name
41 const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED=3003;
42 const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_TIME=3004;//ditto
43 const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_SERIES=3004;
44 const int32_t NIFTI_INTENT_CONNECTIVITY_CONNECTIVITY_TRAJECTORY=3005;//ditto
45 const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_TRAJECTORY=3005;
46 const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_SCALARS=3006;
47 const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_LABELS=3007;
48 const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_SCALAR=3008;
49 const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_DENSE=3009;
50 const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_PARCELLATED=3010;
51 const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_PARCELLATED_SERIES=3011;
52 const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_PARCELLATED_SCALAR=3012;
53
54 const int32_t NIFTI_ECODE_CIFTI=32;
55
56 #define NIFTI2_VERSION(h) \
57 (h).sizeof_hdr == 348 ? 1 : (\
58 (h).sizeof_hdr == 1543569408 ? 1 : (\
59 (h).sizeof_hdr == 540 ? 2 : (\
60 (h).sizeof_hdr == 469893120 ? 2 : 0)))
61
62 #define NIFTI2_NEEDS_SWAP(h) \
63 (h).sizeof_hdr == 469893120 ? 1 : (\
64 (h).sizeof_hdr == 1543569408 ? 1 : 0)
65
66 //hopefully cross-platform solution to byte padding added by some compilers
67 #pragma pack(push)
68 #pragma pack(1)
69
70 /*! \struct nifti_2_header
71 \brief Data structure defining the fields in the nifti2 header.
72 This binary header should be found at the beginning of a valid
73 NIFTI-2 header file.
74 */
75 /*************************/ /************************/ /************/
76 struct nifti_2_header { /* NIFTI-2 usage */ /* NIFTI-1 usage */ /* offset */
77 /*************************/ /************************/ /************/
78 int32_t sizeof_hdr; /*!< MUST be 540 */ /* int32_t sizeof_hdr; (348) */ /* 0 */
79 char magic[8] ; /*!< MUST be valid signature. */ /* char magic[4]; */ /* 4 */
80 int16_t datatype; /*!< Defines data type! */ /* short datatype; */ /* 12 */
81 int16_t bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ /* 14 */
82 int64_t dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ /* 16 */
83 double intent_p1 ; /*!< 1st intent parameter. */ /* float intent_p1; */ /* 80 */
84 double intent_p2 ; /*!< 2nd intent parameter. */ /* float intent_p2; */ /* 88 */
85 double intent_p3 ; /*!< 3rd intent parameter. */ /* float intent_p3; */ /* 96 */
86 double pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ /* 104 */
87 int64_t vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ /* 168 */
88 double scl_slope ; /*!< Data scaling: slope. */ /* float scl_slope; */ /* 176 */
89 double scl_inter ; /*!< Data scaling: offset. */ /* float scl_inter; */ /* 184 */
90 double cal_max; /*!< Max display intensity */ /* float cal_max; */ /* 192 */
91 double cal_min; /*!< Min display intensity */ /* float cal_min; */ /* 200 */
92 double slice_duration;/*!< Time for 1 slice. */ /* float slice_duration; */ /* 208 */
93 double toffset; /*!< Time axis shift. */ /* float toffset; */ /* 216 */
94 int64_t slice_start;/*!< First slice index. */ /* short slice_start; */ /* 224 */
95 int64_t slice_end; /*!< Last slice index. */ /* short slice_end; */ /* 232 */
96 char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ /* 240 */
97 char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ /* 320 */
98 int32_t qform_code ; /*!< NIFTI_XFORM_* code. */ /* short qform_code; */ /* 344 */
99 int32_t sform_code ; /*!< NIFTI_XFORM_* code. */ /* short sform_code; */ /* 348 */
100 double quatern_b ; /*!< Quaternion b param. */ /* float quatern_b; */ /* 352 */
101 double quatern_c ; /*!< Quaternion c param. */ /* float quatern_c; */ /* 360 */
102 double quatern_d ; /*!< Quaternion d param. */ /* float quatern_d; */ /* 368 */
103 double qoffset_x ; /*!< Quaternion x shift. */ /* float qoffset_x; */ /* 376 */
104 double qoffset_y ; /*!< Quaternion y shift. */ /* float qoffset_y; */ /* 384 */
105 double qoffset_z ; /*!< Quaternion z shift. */ /* float qoffset_z; */ /* 392 */
106 double srow_x[4] ; /*!< 1st row affine transform. */ /* float srow_x[4]; */ /* 400 */
107 double srow_y[4] ; /*!< 2nd row affine transform. */ /* float srow_y[4]; */ /* 432 */
108 double srow_z[4] ; /*!< 3rd row affine transform. */ /* float srow_z[4]; */ /* 464 */
109 int32_t slice_code ; /*!< Slice timing order. */ /* char slice_code; */ /* 496 */
110 int32_t xyzt_units ; /*!< Units of pixdim[1..4] */ /* char xyzt_units; */ /* 500 */
111 int32_t intent_code ; /*!< NIFTI_INTENT_* code. */ /* short intent_code; */ /* 504 */
112 char intent_name[16]; /*!< 'name' or meaning of data. */ /* char intent_name[16]; */ /* 508 */
113 char dim_info; /*!< MRI slice ordering. */ /* char dim_info; */ /* 524 */
114 char unused_str[15]; /*!< unused, filled with \0 */ /* 525 */
115 } ; /**** 540 bytes total ****/
116 typedef struct nifti_2_header nifti_2_header ;
117
118 //and restore packing behavior
119 #pragma pack(pop)
120
121 }//namespace
122
123 #endif //__NIFTI2_HEADER
0 /*LICENSE_START*/
1 /*
2 * Copyright (c) 2014, Washington University School of Medicine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "Common/CiftiAssert.h"
28 #include "NiftiIO.h"
29
30 #include "Common/CiftiException.h"
31
32 using namespace std;
33 using namespace cifti;
34
35 void NiftiIO::openRead(const AString& filename)
36 {
37 m_file.open(filename);
38 m_header.read(m_file);
39 if (m_header.getDataType() == DT_BINARY)
40 {
41 throw CiftiException("file uses the binary datatype, which is unsupported: " + filename);
42 }
43 m_dims = m_header.getDimensions();
44 }
45
46 void NiftiIO::writeNew(const AString& filename, const NiftiHeader& header, const int& version, const bool& withRead, const bool& swapEndian)
47 {
48 if (header.getDataType() == DT_BINARY)
49 {
50 throw CiftiException("writing NIFTI with binary datatype is unsupported");
51 }
52 if (withRead)
53 {
54 m_file.open(filename, BinaryFile::READ_WRITE_TRUNCATE);//for cifti on-disk writing, replace structure with along row needs to RMW
55 } else {
56 m_file.open(filename, BinaryFile::WRITE_TRUNCATE);
57 }
58 m_header = header;
59 m_header.write(m_file, version, swapEndian);//the header's getDataOffset() is not what gets written, as it doesn't reflect changes in the extensions
60 m_dims = m_header.getDimensions();
61 }
62
63 void NiftiIO::close()
64 {
65 m_file.close();
66 m_dims.clear();
67 }
68
69 int NiftiIO::getNumComponents() const
70 {
71 switch (m_header.getDataType())
72 {
73 case NIFTI_TYPE_RGB24:
74 return 3;
75 break;
76 case NIFTI_TYPE_COMPLEX64:
77 case NIFTI_TYPE_COMPLEX128:
78 case NIFTI_TYPE_COMPLEX256:
79 return 2;
80 break;
81 case NIFTI_TYPE_INT8:
82 case NIFTI_TYPE_UINT8:
83 case NIFTI_TYPE_INT16:
84 case NIFTI_TYPE_UINT16:
85 case NIFTI_TYPE_INT32:
86 case NIFTI_TYPE_UINT32:
87 case NIFTI_TYPE_FLOAT32:
88 case NIFTI_TYPE_INT64:
89 case NIFTI_TYPE_UINT64:
90 case NIFTI_TYPE_FLOAT64:
91 case NIFTI_TYPE_FLOAT128:
92 return 1;
93 break;
94 default:
95 CiftiAssert(0);
96 throw CiftiException("internal error, report what you did to the developers");
97 }
98 }
99
100 int NiftiIO::numBytesPerElem()
101 {
102 switch (m_header.getDataType())
103 {
104 case NIFTI_TYPE_INT8:
105 case NIFTI_TYPE_UINT8:
106 case NIFTI_TYPE_RGB24:
107 return 1;
108 break;
109 case NIFTI_TYPE_INT16:
110 case NIFTI_TYPE_UINT16:
111 return 2;
112 break;
113 case NIFTI_TYPE_INT32:
114 case NIFTI_TYPE_UINT32:
115 case NIFTI_TYPE_FLOAT32:
116 case NIFTI_TYPE_COMPLEX64:
117 return 4;
118 break;
119 case NIFTI_TYPE_INT64:
120 case NIFTI_TYPE_UINT64:
121 case NIFTI_TYPE_FLOAT64:
122 case NIFTI_TYPE_COMPLEX128:
123 return 8;
124 break;
125 case NIFTI_TYPE_FLOAT128:
126 case NIFTI_TYPE_COMPLEX256:
127 return 16;
128 break;
129 default:
130 CiftiAssert(0);
131 throw CiftiException("internal error, report what you did to the developers");
132 }
133 }
0 #ifndef __NIFTI_IO_H__
1 #define __NIFTI_IO_H__
2
3 /*LICENSE_START*/
4 /*
5 * Copyright (c) 2014, Washington University School of Medicine
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "Common/AString.h"
31
32 #include "Common/ByteSwapping.h"
33 #include "Common/BinaryFile.h"
34 #include "Common/CiftiException.h"
35 #include "Common/CiftiMutex.h"
36 #include "Nifti/NiftiHeader.h"
37
38 //include MultiDimIterator from a private include directory, in case people want to use it with NiftiIO
39 #include "Common/MultiDimIterator.h"
40
41 #include <cmath>
42 #include <limits>
43 #include <vector>
44
45 namespace cifti
46 {
47
48 class NiftiIO
49 {
50 BinaryFile m_file;
51 NiftiHeader m_header;
52 std::vector<int64_t> m_dims;
53 std::vector<char> m_scratch;//scratch memory for byteswapping, type conversion, etc
54 CiftiMutex m_mutex;
55 int numBytesPerElem();//for resizing scratch
56 template<typename TO, typename FROM>
57 void convertRead(TO* out, FROM* in, const int64_t& count);//for reading from file
58 template<typename TO, typename FROM>
59 void convertWrite(TO* out, const FROM* in, const int64_t& count);//for writing to file
60 public:
61 void openRead(const AString& filename);
62 void writeNew(const AString& filename, const NiftiHeader& header, const int& version = 1, const bool& withRead = false, const bool& swapEndian = false);
63 AString getFilename() const { return m_file.getFilename(); }
64 void overrideDimensions(const std::vector<int64_t>& newDims) { m_dims = newDims; }//HACK: deal with reading/writing CIFTI-1's broken headers
65 void close();
66 const NiftiHeader& getHeader() const { return m_header; }
67 const std::vector<int64_t>& getDimensions() const { return m_dims; }
68 int getNumComponents() const;
69 //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
70 //NOTE: you need to provide storage for all components within the range, if getNumComponents() == 3 and fullDims == 0, you need 3 elements allocated
71 template<typename T>
72 void readData(T* dataOut, const int& fullDims, const std::vector<int64_t>& indexSelect, const bool& tolerateShortRead = false);
73 template<typename T>
74 void writeData(const T* dataIn, const int& fullDims, const std::vector<int64_t>& indexSelect);
75 };
76
77 template<typename T>
78 void NiftiIO::readData(T* dataOut, const int& fullDims, const std::vector<int64_t>& indexSelect, const bool& tolerateShortRead)
79 {
80 if (fullDims < 0) throw CiftiException("NiftiIO: fulldims must not be negative");
81 if (fullDims > (int)m_dims.size()) throw CiftiException("NiftiIO: fulldims must not be greater than number of dimensions");
82 if ((size_t)fullDims + indexSelect.size() != m_dims.size())
83 {//could be >=, but should catch more stupid mistakes as ==
84 throw CiftiException("NiftiIO: fulldims plus length of indexSelect must equal number of dimensions");
85 }
86 int64_t numElems = getNumComponents();//for now, calculate read size on the fly, as the read call will be the slowest part
87 int curDim;
88 for (curDim = 0; curDim < fullDims; ++curDim)
89 {
90 numElems *= m_dims[curDim];
91 }
92 int64_t numDimSkip = numElems, numSkip = 0;
93 for (; curDim < (int)m_dims.size(); ++curDim)
94 {
95 if (indexSelect[curDim - fullDims] < 0) throw CiftiException("NiftiIO: indices must not be negative");
96 if (indexSelect[curDim - fullDims] >= m_dims[curDim]) throw CiftiException("NiftiIO: index exceeds nifti dimension length");
97 numSkip += indexSelect[curDim - fullDims] * numDimSkip;
98 numDimSkip *= m_dims[curDim];
99 }
100 CiftiMutexLocker locked(&m_mutex);//protect starting with resizing until we are done converting, because we use an internal variable for scratch space
101 //we can't guarantee that the output memory is enough to use as scratch space, as we might be doing a narrowing conversion
102 //we are doing FILE ACCESS, so cpu performance isn't really something to worry about
103 m_scratch.resize(numElems * numBytesPerElem());
104 m_file.seek(numSkip * numBytesPerElem() + m_header.getDataOffset());
105 int64_t numRead = 0;
106 m_file.read(m_scratch.data(), m_scratch.size(), &numRead);
107 if ((numRead != (int64_t)m_scratch.size() && !tolerateShortRead) || numRead < 0)//for now, assume read giving -1 is always a problem
108 {
109 throw CiftiException("error while reading from file '" + m_file.getFilename() + "'");
110 }
111 switch (m_header.getDataType())
112 {
113 case NIFTI_TYPE_UINT8:
114 case NIFTI_TYPE_RGB24://handled by components
115 convertRead(dataOut, (uint8_t*)m_scratch.data(), numElems);
116 break;
117 case NIFTI_TYPE_INT8:
118 convertRead(dataOut, (int8_t*)m_scratch.data(), numElems);
119 break;
120 case NIFTI_TYPE_UINT16:
121 convertRead(dataOut, (uint16_t*)m_scratch.data(), numElems);
122 break;
123 case NIFTI_TYPE_INT16:
124 convertRead(dataOut, (int16_t*)m_scratch.data(), numElems);
125 break;
126 case NIFTI_TYPE_UINT32:
127 convertRead(dataOut, (uint32_t*)m_scratch.data(), numElems);
128 break;
129 case NIFTI_TYPE_INT32:
130 convertRead(dataOut, (int32_t*)m_scratch.data(), numElems);
131 break;
132 case NIFTI_TYPE_UINT64:
133 convertRead(dataOut, (uint64_t*)m_scratch.data(), numElems);
134 break;
135 case NIFTI_TYPE_INT64:
136 convertRead(dataOut, (int64_t*)m_scratch.data(), numElems);
137 break;
138 case NIFTI_TYPE_FLOAT32:
139 case NIFTI_TYPE_COMPLEX64://components
140 convertRead(dataOut, (float*)m_scratch.data(), numElems);
141 break;
142 case NIFTI_TYPE_FLOAT64:
143 case NIFTI_TYPE_COMPLEX128:
144 convertRead(dataOut, (double*)m_scratch.data(), numElems);
145 break;
146 case NIFTI_TYPE_FLOAT128:
147 case NIFTI_TYPE_COMPLEX256:
148 convertRead(dataOut, (long double*)m_scratch.data(), numElems);
149 break;
150 default:
151 throw CiftiException("internal error, tell the developers what you just tried to do");
152 }
153 }
154
155 template<typename T>
156 void NiftiIO::writeData(const T* dataIn, const int& fullDims, const std::vector<int64_t>& indexSelect)
157 {
158 if (fullDims < 0) throw CiftiException("NiftiIO: fulldims must not be negative");
159 if (fullDims > (int)m_dims.size()) throw CiftiException("NiftiIO: fulldims must not be greater than number of dimensions");
160 if ((size_t)fullDims + indexSelect.size() != m_dims.size())
161 {//could be >=, but should catch more stupid mistakes as ==
162 throw CiftiException("NiftiIO: fulldims plus length of indexSelect must equal number of dimensions");
163 }
164 int64_t numElems = getNumComponents();//for now, calculate read size on the fly, as the read call will be the slowest part
165 int curDim;
166 for (curDim = 0; curDim < fullDims; ++curDim)
167 {
168 numElems *= m_dims[curDim];
169 }
170 int64_t numDimSkip = numElems, numSkip = 0;
171 for (; curDim < (int)m_dims.size(); ++curDim)
172 {
173 if (indexSelect[curDim - fullDims] < 0) throw CiftiException("NiftiIO: indices must not be negative");
174 if (indexSelect[curDim - fullDims] >= m_dims[curDim]) throw CiftiException("NiftiIO: index exceeds nifti dimension length");
175 numSkip += indexSelect[curDim - fullDims] * numDimSkip;
176 numDimSkip *= m_dims[curDim];
177 }
178 CiftiMutexLocker locked(&m_mutex);//protect starting with resizing until we are done writing, because we use an internal variable for scratch space
179 //we are doing FILE ACCESS, so cpu performance isn't really something to worry about
180 m_scratch.resize(numElems * numBytesPerElem());
181 m_file.seek(numSkip * numBytesPerElem() + m_header.getDataOffset());
182 switch (m_header.getDataType())
183 {
184 case NIFTI_TYPE_UINT8:
185 case NIFTI_TYPE_RGB24://handled by components
186 convertWrite((uint8_t*)m_scratch.data(), dataIn, numElems);
187 break;
188 case NIFTI_TYPE_INT8:
189 convertWrite((int8_t*)m_scratch.data(), dataIn, numElems);
190 break;
191 case NIFTI_TYPE_UINT16:
192 convertWrite((uint16_t*)m_scratch.data(), dataIn, numElems);
193 break;
194 case NIFTI_TYPE_INT16:
195 convertWrite((int16_t*)m_scratch.data(), dataIn, numElems);
196 break;
197 case NIFTI_TYPE_UINT32:
198 convertWrite((uint32_t*)m_scratch.data(), dataIn, numElems);
199 break;
200 case NIFTI_TYPE_INT32:
201 convertWrite((int32_t*)m_scratch.data(), dataIn, numElems);
202 break;
203 case NIFTI_TYPE_UINT64:
204 convertWrite((uint64_t*)m_scratch.data(), dataIn, numElems);
205 break;
206 case NIFTI_TYPE_INT64:
207 convertWrite((int64_t*)m_scratch.data(), dataIn, numElems);
208 break;
209 case NIFTI_TYPE_FLOAT32:
210 case NIFTI_TYPE_COMPLEX64://components
211 convertWrite((float*)m_scratch.data(), dataIn, numElems);
212 break;
213 case NIFTI_TYPE_FLOAT64:
214 case NIFTI_TYPE_COMPLEX128:
215 convertWrite((double*)m_scratch.data(), dataIn, numElems);
216 break;
217 case NIFTI_TYPE_FLOAT128:
218 case NIFTI_TYPE_COMPLEX256:
219 convertWrite((long double*)m_scratch.data(), dataIn, numElems);
220 break;
221 default:
222 throw CiftiException("internal error, tell the developers what you just tried to do");
223 }
224 m_file.write(m_scratch.data(), m_scratch.size());
225 }
226
227 template<typename TO, typename FROM>
228 void NiftiIO::convertRead(TO* out, FROM* in, const int64_t& count)
229 {
230 if (m_header.isSwapped())
231 {
232 ByteSwapping::swapArray(in, count);
233 }
234 double mult, offset;
235 bool doScale = m_header.getDataScaling(mult, offset);
236 if (std::numeric_limits<TO>::is_integer)//do round to nearest when integer output type
237 {
238 if (doScale)
239 {
240 for (int64_t i = 0; i < count; ++i)
241 {
242 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
243 }
244 } else {
245 for (int64_t i = 0; i < count; ++i)
246 {
247 out[i] = (TO)floor(0.5 + in[i]);
248 }
249 }
250 } else {
251 if (doScale)
252 {
253 for (int64_t i = 0; i < count; ++i)
254 {
255 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
256 }
257 } else {
258 for (int64_t i = 0; i < count; ++i)
259 {
260 out[i] = (TO)in[i];//explicit cast to make sure the compiler doesn't squawk
261 }
262 }
263 }
264 }
265
266 template<typename TO, typename FROM>
267 void NiftiIO::convertWrite(TO* out, const FROM* in, const int64_t& count)
268 {
269 double mult, offset;
270 bool doScale = m_header.getDataScaling(mult, offset);
271 if (std::numeric_limits<TO>::is_integer)//do round to nearest when integer output type
272 {
273 if (doScale)
274 {
275 for (int64_t i = 0; i < count; ++i)
276 {
277 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
278 }
279 } else {
280 for (int64_t i = 0; i < count; ++i)
281 {
282 out[i] = (TO)floor(0.5 + in[i]);
283 }
284 }
285 } else {
286 if (doScale)
287 {
288 for (int64_t i = 0; i < count; ++i)
289 {
290 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
291 }
292 } else {
293 for (int64_t i = 0; i < count; ++i)
294 {
295 out[i] = (TO)in[i];//explicit cast to make sure the compiler doesn't squawk
296 }
297 }
298 }
299 if (m_header.isSwapped()) ByteSwapping::swapArray(out, count);
300 }
301
302 }
303
304 #endif //__NIFTI_IO_H__