[Debian-med-packaging] [mrtrix] 01/03: Imported Upstream version 0.2.12

Michael Hanke mih at moszumanska.debian.org
Sun Mar 9 06:43:27 UTC 2014


This is an automated email from the git hooks/post-receive script.

mih pushed a commit to branch master
in repository mrtrix.

commit c702e794502a79b131ed3a7155fb83fdc24d3230
Author: Michael Hanke <michael.hanke at gmail.com>
Date:   Sat Mar 8 20:25:50 2014 +0100

    Imported Upstream version 0.2.12
---
 Doxyfile                                       |   2 +-
 build                                          | 544 +++++++++++++------------
 cmd/cat_tracks.cpp                             |  94 +++++
 cmd/csdeconv.cpp                               |   2 +-
 cmd/disp_profile.cpp                           |   2 +-
 cmd/dwi2tensor.cpp                             |   2 +-
 cmd/estimate_response.cpp                      |   2 +-
 cmd/find_SH_peaks.cpp                          |   6 +-
 cmd/gen_ROI.cpp                                |   2 +-
 cmd/median3D.cpp                               |   6 +-
 cmd/mradd.cpp                                  |   2 +-
 cmd/mrcat.cpp                                  |   2 +-
 cmd/mrconvert.cpp                              |  24 +-
 cmd/mrstats.cpp                                |   6 +-
 cmd/mrtransform.cpp                            |   6 +-
 cmd/mrview.cpp                                 |   1 +
 cmd/streamtrack.cpp                            |  12 +-
 cmd/threshold.cpp                              |   8 +-
 cmd/tracks2prob.cpp                            |   6 +-
 doc/appendix/config.html                       |   6 +-
 doc/appendix/index.html                        |   6 +-
 doc/appendix/mrtrix.html                       |   6 +-
 doc/appendix/refs.html                         |   6 +-
 doc/commands/average.html                      |   8 +-
 doc/commands/{mradd.html => cat_tracks.html}   |  26 +-
 doc/commands/cleanup_ANTS_warp.html            |   8 +-
 doc/commands/csdeconv.html                     |   6 +-
 doc/commands/dicom_filename.html               |   6 +-
 doc/commands/dir2amp.html                      |   6 +-
 doc/commands/disp_profile.html                 |   6 +-
 doc/commands/dwi2SH.html                       |   6 +-
 doc/commands/dwi2tensor.html                   |   6 +-
 doc/commands/erode.html                        |   6 +-
 doc/commands/estimate_response.html            |   6 +-
 doc/commands/filter_tracks.html                |   6 +-
 doc/commands/find_SH_peaks.html                |   6 +-
 doc/commands/gen_ROI.html                      |   6 +-
 doc/commands/gen_WM_mask.html                  |   6 +-
 doc/commands/gen_unit_warp.html                |   6 +-
 doc/commands/gendir.html                       |   6 +-
 doc/commands/import_tracks.html                |   6 +-
 doc/commands/index.html                        |   5 +-
 doc/commands/median3D.html                     |   6 +-
 doc/commands/mrabs.html                        |   6 +-
 doc/commands/mradd.html                        |   6 +-
 doc/commands/mrcat.html                        |   6 +-
 doc/commands/mrconvert.html                    |  14 +-
 doc/commands/mrinfo.html                       |   6 +-
 doc/commands/mrmult.html                       |   6 +-
 doc/commands/mrstats.html                      |   6 +-
 doc/commands/mrtransform.html                  |   6 +-
 doc/commands/mrview.html                       |   6 +-
 doc/commands/normalise_tracks.html             |   6 +-
 doc/commands/read_dicom.html                   |   6 +-
 doc/commands/read_ximg.html                    |   6 +-
 doc/commands/resample_tracks.html              |   6 +-
 doc/commands/sample_tracks.html                |   6 +-
 doc/commands/sdeconv.html                      |   6 +-
 doc/commands/select_tracks.html                |   6 +-
 doc/commands/streamtrack.html                  |   6 +-
 doc/commands/tensor2ADC.html                   |   6 +-
 doc/commands/tensor2FA.html                    |   6 +-
 doc/commands/tensor2vector.html                |   6 +-
 doc/commands/tensor_metric.html                |   6 +-
 doc/commands/threshold.html                    |   6 +-
 doc/commands/track_info.html                   |   6 +-
 doc/commands/tracks2prob.html                  |   6 +-
 doc/commands/tracks2vtk.html                   |   6 +-
 doc/commands/truncate_tracks.html              |   6 +-
 doc/faq.html                                   |   6 +-
 doc/general/cmdline.html                       |   6 +-
 doc/general/formats.html                       |   6 +-
 doc/general/index.html                         |   6 +-
 doc/general/mrview.html                        |   6 +-
 doc/general/overview.html                      |   6 +-
 doc/index.html                                 |   8 +-
 doc/install/index.html                         |   6 +-
 doc/install/macosx.html                        |   6 +-
 doc/install/unix.html                          |   6 +-
 doc/install/windows.html                       |   6 +-
 doc/intro.html                                 |   6 +-
 doc/tractography/dwi.html                      |   6 +-
 doc/tractography/index.html                    |   6 +-
 doc/tractography/preprocess.html               |   6 +-
 doc/tractography/roi.html                      |   6 +-
 doc/tractography/tracking.html                 |   6 +-
 lib/file/dicom/image.cpp                       |   2 +-
 lib/file/dicom/mapper.cpp                      |  72 +++-
 lib/image/format/analyse.cpp                   |   4 +-
 lib/image/format/nifti1.cpp                    |   4 +-
 lib/image/name_parser.cpp                      |  11 +-
 lib/image/object.cpp                           |   2 +-
 lib/image/position.h                           |   4 +-
 lib/math/vector.h                              |   4 +-
 lib/mrtrix.cpp                                 |   2 +-
 lib/mrtrix.h                                   |   8 +-
 lib/svn_revision.h                             |   2 +-
 matlab/write_mrtrix.m                          |   4 +-
 src/dwi/SH.cpp                                 |   8 +-
 src/dwi/gradient.cpp                           |  44 +-
 src/dwi/render_frame.cpp                       |   4 +-
 src/dwi/render_frame.h                         |   2 +-
 src/dwi/renderer.cpp                           |   2 +-
 src/dwi/sdeconv/MCMC.cpp                       |   4 +-
 src/dwi/shells.h                               | 269 ++++++++++++
 src/dwi/tractography/tracker/base.cpp          |   1 +
 src/dwi/tractography/tracker/base.h            |  17 +-
 src/dwi/tractography/tracker/dt_stream.h       |   3 +-
 src/dwi/tractography/tracker/sd_prob.cpp       |  27 +-
 src/dwi/tractography/tracker/sd_prob.h         |   7 +
 src/dwi/tractography/tracker/sd_stream.cpp     |  25 +-
 src/dwi/tractography/tracker/sd_stream.h       |  24 --
 src/histogram.cpp                              |   4 +-
 src/mrview/image.cpp                           |   4 +-
 src/mrview/pane.cpp                            |   3 +-
 src/mrview/sidebar/overlay.cpp                 |   4 +-
 src/mrview/sidebar/roi_analysis.cpp            |  45 +-
 src/mrview/sidebar/roi_analysis.h              |  13 +-
 src/mrview/sidebar/roi_analysis/roi_list.cpp   | 388 ++++++++++++++++--
 src/mrview/sidebar/roi_analysis/roi_list.h     |  70 +++-
 src/mrview/sidebar/tractography.cpp            |   1 +
 src/mrview/sidebar/tractography/roi_list.cpp   |   1 +
 src/mrview/sidebar/tractography/track_list.cpp |   1 +
 src/mrview/window.cpp                          |   1 +
 sysconf/darwin.py                              |  14 +-
 sysconf/linux.py                               |   4 +-
 126 files changed, 1584 insertions(+), 689 deletions(-)

diff --git a/Doxyfile b/Doxyfile
index 15bb09b..d2b49fe 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME           = MRtrix
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 0.2.11
+PROJECT_NUMBER         = 0.2.12
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
diff --git a/build b/build
index 149e862..29bffa0 100755
--- a/build
+++ b/build
@@ -1,6 +1,6 @@
-#!/usr/bin/python2
+#!/usr/bin/python
 
-import platform, sys, os, time, threading, subprocess, shutil, stat
+import platform, sys, os, time, threading, subprocess, shutil, stat, codecs
 
 system = None
 debug = False
@@ -23,6 +23,27 @@ linkto = ''
 print_help = False
 
 
+if sys.version_info[0] == 2:
+  def as_str (msg):
+    if not isinstance (msg, unicode): msg = unicode(msg, 'utf-8')
+    return msg
+  def report (msg):
+    sys.stderr.write (as_str (msg).encode ('utf-8', 'ignore'))
+else: 
+  def as_str (msg):
+    if not isinstance (msg, str): msg = msg.decode (errors='ignore')
+    return msg
+  def report (msg):
+    sys.stderr.write (as_str (msg))
+
+
+
+def fopen (filename, mode):
+  return codecs.open (filename, mode, encoding='utf-8')
+
+
+
+
 # parse command-line:
 
 for arg in sys.argv[1:]:
@@ -47,22 +68,21 @@ for arg in sys.argv[1:]:
         else: linkto = args[1]
     elif args[0] == '-system': 
       if len (args) != 2: 
-        print 'missing argument to option "-system"'
+        report ('missing argument to option "-system"')
         exit (1)
       system = args[1]
     elif arg[0] == '-':
-      print 'unknown command-line option "' + arg + '"'
-      sys.stdout.flush()
+      report ('unknown command-line option "' + arg + '"')
       sys.exit (1)
     else: 
       targets.append(arg)
 
 if system == None: system = platform.system()
-exec 'from sysconf.' + system.lower() + ' import *'
+exec ('from sysconf.' + system.lower() + ' import *')
 
 
 if print_help:
-  print """usage: ./build [ options ] [ target ]... 
+  report ("""usage: ./build [ options ] [ target ]... 
 
 A target can be any file generated by the compilation process. For example:
 
@@ -104,35 +124,37 @@ Acceptable options are:
   -profile          generate code with profiling enabled (for use with gprof)
   -system=X         specify the architecture to build
 (default is native)
-"""
+""")
   exit (0)
 
 
 # check if we are compiling a separate project:
 
 mrtrix_dir = os.path.dirname (os.path.realpath(sys.argv[0]))
-if os.path.abspath(mrtrix_dir) == os.path.abspath(os.getcwd()): mrtrix_dir = None
+if os.path.abspath(mrtrix_dir) == os.path.abspath(os.getcwd()): 
+  mrtrix_dir = None
 else:
   if verbose: 
-    print 'compiling separate project against "' + mrtrix_dir + '"'
-    print ''
+    report ('compiling separate project against "' + mrtrix_dir + '''"
+
+''')
   lib_dir = os.path.join (mrtrix_dir, lib_dir)
   include_paths = [ misc_dir ]
   misc_dir = os.path.join (mrtrix_dir, misc_dir)
+  svn_revision_file = os.path.join (mrtrix_dir, svn_revision_file)
   
 
 
 # get version info:
 
-f = open (os.path.join (lib_dir, 'mrtrix.h'), 'r')
-for line in f:
-  line = line.split()
-  if len(line) != 3: continue
-  if line[0] == '#define':
-    if line[1] == 'MRTRIX_MAJOR_VERSION': major = line[2]
-    elif line[1] == 'MRTRIX_MINOR_VERSION': minor = line[2]
-    elif line[1] == 'MRTRIX_MICRO_VERSION': micro = line[2]
-f.close()
+with fopen (os.path.join (lib_dir, 'mrtrix.h'), 'r') as f:
+  for line in f:
+    line = line.split()
+    if len(line) != 3: continue
+    if line[0] == '#define':
+      if line[1] == 'MRTRIX_MAJOR_VERSION': major = line[2]
+      elif line[1] == 'MRTRIX_MINOR_VERSION': minor = line[2]
+      elif line[1] == 'MRTRIX_MICRO_VERSION': micro = line[2]
 
 libname += '-' + major + '_' + minor + '_' + micro
 
@@ -159,30 +181,42 @@ if profile:
   ld_lib_flags = ld_lib_flags_profile
 
 
+# used to set install_name on MacOSX:
+ld_lib_flags = [ entry.replace ('LIBNAME', libname) for entry in ld_lib_flags ]
+
 
 # SVN revision number:
-if not os.path.exists (svn_revision_file):
-  with open(svn_revision_file, 'wb') as fd:
-    fd.write('')
 svn_revision = '#define SVN_REVISION 0\n'
+if not os.path.exists (svn_revision_file):
+  with fopen(svn_revision_file, 'w') as fd:
+    fd.write (svn_revision)
 
 try:
-  process = subprocess.Popen ([ 'svn', 'info', '--xml' ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-  if process.wait() != 0: raise 0
-  svn_info = process.stdout.read().translate (None, ' \t\n')
+  svn_cmd = [ 'svn', 'info', '--xml' ]
+  if mrtrix_dir: 
+    svn_cmd += [ mrtrix_dir ]
+  process = subprocess.Popen (svn_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  if process.wait() != 0: 
+    raise Exception
+  svn_info = as_str (process.stdout.read()).replace(' ','').replace('\t','').replace('\n','')
   svn_revision_index = svn_info.find ('revision=')
   svn_revision = 0
-  if svn_revision_index > 0:
-    svn_revision = svn_info[svn_revision_index:-1].split('"')[1]
+  if not svn_revision_index > 0:
+    if verbose:
+      report ('WARNING: error parsing output of "svn info" - SVN revision number may be incorrect')
+    raise Exception
+  svn_revision = svn_info[svn_revision_index:-1].split('"')[1]
   if verbose:
-    print svn_revision
+    report ('SVN revision: ' + svn_revision + os.linesep + os.linesep)
   svn_revision = '#define SVN_REVISION ' + svn_revision + '\n'
-except:
-  pass
 
-if svn_revision != open(svn_revision_file).read():
-  with open(svn_revision_file, 'wb') as fd:
-    fd.write (svn_revision)
+  if svn_revision != fopen (svn_revision_file, 'r').read():
+    with fopen (svn_revision_file, 'w') as fd:
+      fd.write (svn_revision)
+
+except:
+  if verbose:
+    report ('failed to retrieve current SVN revision number - leaving as-is' + os.linesep + os.linesep)
 
 
 
@@ -204,10 +238,9 @@ def pkg (package, target, extras = None):
   if extras: cmd += extras
   process = subprocess.Popen (cmd, stdout=subprocess.PIPE, env=pkgconfig_env)
   if process.wait() != 0:
-    print 'WARNING: unable to find', package, 'configuration'
-    sys.stdout.flush()
+    report ('WARNING: unable to find', package, 'configuration' + os.linesep)
     return None
-  return process.stdout.read().split()
+  return as_str(process.stdout.read()).split()
 
 cflags_glib = pkg (pkgconfig_glib, 'cflags')
 cflags_gtk = pkg (pkgconfig_gtk, 'cflags')
@@ -217,13 +250,14 @@ libs_gtk = pkg (pkgconfig_gtk, 'libs')
 libs_gtkgl = pkg (pkgconfig_gtk, 'libs', [ pkgconfig_gl ]) + ld_flags_gl
 
 if verbose:
-  print 'Settings:'
-  print '  GLib cflags:  ', ' '.join (cflags_glib)
-  print '  GTK cflags:   ', ' '.join (cflags_gtk)
-  print '  GTK/GL cflags:', ' '.join (cflags_gtkgl)
-  print '  GLib libs:    ', ' '.join (libs_glib)
-  print '  GTK libs:     ', ' '.join (libs_gtk)
-  print '  GTK/GL libs:  ', ' '.join (libs_gtkgl)
+  report ('''Settings:
+  GLib cflags:   ''' + ' '.join (cflags_glib) + '''
+  GTK cflags:    ''' + ' '.join (cflags_gtk) + '''
+  GTK/GL cflags: ''' + ' '.join (cflags_gtkgl) + '''
+  GLib libs:     ''' + ' '.join (libs_glib) + '''
+  GTK libs:      ''' + ' '.join (libs_gtk) + '''
+  GTK/GL libs:   ''' + ' '.join (libs_gtkgl) + '''
+''')
 
 
 
@@ -330,12 +364,13 @@ class Entry:
 
 
   def display (self):
-    print '[' + self.action + '] ' + self.name + ' (' + str(self.timestamp) + ' - ' + str(self.dep_timestamp) + ')',
-    if not self.timestamp or self.timestamp < self.dep_timestamp: print '[REBUILD]',
-    print ':'
-    print '  deps: ', ' '.join(self.deps)
-    print '  command: ', ' '.join(self.cmd)
-    sys.stdout.flush()
+    report ('[' + self.action + '] ' + self.name + ' (' + str(self.timestamp) + ' - ' + str(self.dep_timestamp) + ')')
+    if not self.timestamp or self.timestamp < self.dep_timestamp: 
+      report (' [REBUILD]')
+    report (''':
+    deps: ''' + ' '.join(self.deps) + '''
+    command: ''' + ' '.join(self.cmd) + '''
+''')
 
 
 
@@ -349,7 +384,7 @@ class Entry:
 
 def default_targets():
   if not os.path.isdir (cmd_dir): 
-    print 'ERROR: no "cmd" folder - unable to determine default targets'
+    report ('ERROR: no "cmd" folder - unable to determine default targets' + os.linesep)
     exit (1)
   target_list = []
   for entry in os.listdir (cmd_dir):
@@ -383,41 +418,34 @@ def fillin (template, keyvalue):
 
 
 def execute (message, cmd):
-  print message
-  sys.stdout.flush()
+  report (message + os.linesep)
   if verbose: 
-    print ' '.join(cmd)
-    sys.stdout.flush()
+    report (' '.join(cmd) + os.linesep)
 
   try: 
     process = subprocess.Popen (cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     ( stdoutdata, stderrdata ) = process.communicate()
     if process.returncode != 0:
-      print ''
-      print 'ERROR:', message
-      print ''
-      print ' '.join(cmd)
-      print ''
-      print 'failed with output:'
-      print ''
-      print stderrdata
-      sys.stdout.flush()
+      report ('''
+ERROR: ''' + message + '''
+
+''' + ' '.join(cmd) + '''
+
+failed with output:
+
+''' + as_str (stderrdata) + os.linesep)
       return 1
     if len(stdoutdata):
-      print 'STDOUT:', message
-      print stdoutdata
-      sys.stdout.flush()
+      report ('STDOUT: ' + message + os.linesep + as_str (stdoutdata) + os.linesep)
     if len(stderrdata):
-      print 'STDERR:', message
-      print stderrdata
-      sys.stdout.flush()
+      report ('STDERR: ' + message + os.linesep + as_str (stderrdata) + os.linesep)
 
   except OSError:
-    print cmd[0] + ': command not found'
-    sys.stdout.flush()
+    report (cmd[0] + ': command not found' + os.linesep)
     return 1
   except Exception as inst:
-    print 'unexpected exception: ' + str(inst)
+    report ('unexpected exception: ' + inst.args[0] + os.linesep)
+    return 1
 
 
 
@@ -431,28 +459,25 @@ def list_headers (file):
     if file == gl: file_flags[file] = 3
     else: file_flags[file] = 1
     if not os.path.exists (file):
-      print 'ERROR: cannot find file "' + file + '"'
-      sys.stdout.flush()
+      report ('ERROR: cannot find file "' + file + '"' + os.linesep)
       exit(1)
-    fd = open (file, 'r')
-    for line in fd:
-      line = line.strip()
-      if line.startswith('#include'):
-        line = line[8:].strip()
-        if line.startswith('<gtk') or line.startswith('<gdk'): file_flags[file] = max (file_flags[file], 2)
-        elif line[0] == '"':
-          line = line[1:].rstrip('"')
-          for path in include_paths:
-            if os.path.exists (os.path.join (path, line)):
-              line = os.path.join (path, line)
-              headers[file].add (line)
-              [ headers[file].add(entry) for entry in list_headers(line) ]
-              break
-          else:
-            print 'ERROR: cannot find header file \"' + line + '\" (from file \"' + file + '\")'
-            sys.stdout.flush()
-            exit(1)
-    fd.close()
+    with fopen (file, 'r') as fd:
+      for line in fd:
+        line = line.strip()
+        if line.startswith('#include'):
+          line = line[8:].strip()
+          if line.startswith('<gtk') or line.startswith('<gdk'): file_flags[file] = max (file_flags[file], 2)
+          elif line[0] == '"':
+            line = line[1:].rstrip('"')
+            for path in include_paths:
+              if os.path.exists (os.path.join (path, line)):
+                line = os.path.join (path, line)
+                headers[file].add (line)
+                [ headers[file].add(entry) for entry in list_headers(line) ]
+                break
+            else:
+              report ('ERROR: cannot find header file \"' + line + '\" (from file \"' + file + '\")' + os.linesep)
+              exit(1)
     flags =  [ file_flags[file] ] + [ file_flags[entry] for entry in headers[file] ]
     if len(flags): file_flags[file] = max (flags)
 
@@ -508,7 +533,7 @@ def build_next (id):
   
       target = todo[current]
       if execute ('[' + target.action + '] ' + target.name, target.cmd):
-        print 'STOP'
+        report ('STOP' + os.linesep)
         stop = 2
         return
 
@@ -559,8 +584,7 @@ def gen_command_html_help ():
 
   # generate each program's individual page:
   for n in range (0, len(binaries)):
-    print '[DOC] ' + binaries[n]
-    sys.stdout.flush()
+    report ('[DOC] ' + binaries[n] + os.linesep)
      
     env = dict(os.environ)
     if platform.system().lower() == 'windows':
@@ -570,90 +594,87 @@ def gen_command_html_help ():
     process = subprocess.Popen ([os.path.join(bin_dir,binaries[n]), '__print_full_usage__' ], stdout=subprocess.PIPE, env=env) 
     a = process.communicate()
     if process.returncode != 0:
-      print 'ERROR: unable to execute', binaries[n], ' - aborting'
-      sys.stdout.flush()
+      report ('ERROR: unable to execute', binaries[n], ' - aborting' + os.linesep)
       return 1
-    H = a[0]
-    H = H.splitlines()
-
-    fid = open (os.path.join (doc_dir, 'commands', binaries[n] + '.html'), 'wb')
-
-    if n == 0: prev = 'index'
-    else: prev = binaries[n-1]
-
-    if n == len(binaries)-1: next = 'index'
-    else: next = binaries[n+1]
-
-    start_html (fid, binaries[n], prev, 'index', '../index', next)
-    fid.write ('<h2>Description</h2>\n')
-
-    line = 0
-    while not H[line].startswith ('ARGUMENT') and not H[line].startswith('OPTION'):
-      if len(description) <= n: description.append (H[line])
-      fid.write ('<p>\n' + H[line] + '\n</p>\n')
-      line += 1
-
-    arg = []
-    opt = []
-    while line < len(H)-2:
-      if not H[line].startswith ('ARGUMENT') and not H[line].startswith ('OPTION'):
-        print 'ERROR: malformed usage for executable "' + binaries[n] + '" - aborting'
-        sys.stdout.flush()
-        return 1
-
-      if H[line].startswith ('ARGUMENT'):
-        S = H[line].split (None, 5)
-        A = [ S[1], int(S[2]), int(S[3]), S[4], H[line+1], H[line+2] ]
-        if len(opt) > 0: opt[-1].append (A)
-        else: arg.append(A)
-
-      elif H[line].startswith ('OPTION'):
-        S = H[line].split (None, 4)
-        A = [ S[1], int(S[2]), int(S[3]), H[line+1], H[line+2] ]
-        opt.append (A)
-
-      line += 3
-
-    fid.write ('<p class=indented><strong>syntax:</strong>     ' + binaries[n] + ' [ options ] ')
-    for A in arg:
-      if A[1] == 0: fid.write ('[ ')
-      fid.write (A[0] + ' ')
-      if A[2] == 1: fid.write ('[ ' + A[0] + ' ... ')
-      if A[1] == 0 or A[2] == 1: fid.write ('] ')
-    fid.write ('</p>\n<h2>Arguments</h2>\n<table class=args>\n')
-
-
-    for A in arg:
-      fid.write ('<tr><td><b>' + A[0] + '</b>')
-      if A[1] == 0 or A[2] == 1:
-        fid.write (' [ ')
-        if A[1] == 0: 
-          fid.write ('optional')
-          if A[2] == 1: fid.write (', ')
-        if A[2] == 1: fid.write ('multiples allowed')
-        fid.write (' ]')
-      fid.write ('</td>\n<td>' + A[5] + '</td></tr>\n')
-    fid.write ('</table>\n')
-
-    fid.write ('<h2>Options</h2>\n<table class=args>\n')
-    for O in opt:
-      fid.write ('<tr><td nowrap><b>-' + O[0] + '</b>')
-      for A in O[5:]: fid.write (' <i>' + A[0] + '</i>')
-      fid.write ('</td>\n<td>' + O[4])
-      if len(O) > 5:
-        fid.write ('\n<table class=opts>\n')
-        for A in O[5:]:
-          fid.write ('<tr><td><i>' + A[0] + '</i></td>\n<td>' + A[5] + '</td></tr>\n')
-        fid.write ('</table>')
-      fid.write ('</td></tr>\n')
-    fid.write ('</table>\n')
-
-    cmd_file = os.path.join (cmd_dir, binaries[n] + cpp_suffix)
-    timestamp = mtime (cmd_file)
-    if timestamp > most_recent_timestamp: 
-      most_recent_timestamp = timestamp
-      most_recent_cmd = cmd_file
-    fid.write ('''
+    H = as_str (a[0]).splitlines()
+
+    with fopen (os.path.join (doc_dir, 'commands', binaries[n] + '.html'), 'w') as fid:
+
+      if n == 0: prev = 'index'
+      else: prev = binaries[n-1]
+
+      if n == len(binaries)-1: next = 'index'
+      else: next = binaries[n+1]
+
+      start_html (fid, binaries[n], prev, 'index', '../index', next)
+      fid.write ('<h2>Description</h2>\n')
+
+      line = 0
+      while not H[line].startswith ('ARGUMENT') and not H[line].startswith('OPTION'):
+        if len(description) <= n: description.append (H[line])
+        fid.write ('<p>\n' + H[line] + '\n</p>\n')
+        line += 1
+
+      arg = []
+      opt = []
+      while line < len(H)-2:
+        if not H[line].startswith ('ARGUMENT') and not H[line].startswith ('OPTION'):
+          report ('ERROR: malformed usage for executable "' + binaries[n] + '" - aborting' + os.linesep)
+          return 1
+
+        if H[line].startswith ('ARGUMENT'):
+          S = H[line].split (None, 5)
+          A = [ S[1], int(S[2]), int(S[3]), S[4], H[line+1], H[line+2] ]
+          if len(opt) > 0: opt[-1].append (A)
+          else: arg.append(A)
+
+        elif H[line].startswith ('OPTION'):
+          S = H[line].split (None, 4)
+          A = [ S[1], int(S[2]), int(S[3]), H[line+1], H[line+2] ]
+          opt.append (A)
+
+        line += 3
+
+      fid.write ('<p class=indented><strong>syntax:</strong>     ' + binaries[n] + ' [ options ] ')
+      for A in arg:
+        if A[1] == 0: fid.write ('[ ')
+        fid.write (A[0] + ' ')
+        if A[2] == 1: fid.write ('[ ' + A[0] + ' ... ')
+        if A[1] == 0 or A[2] == 1: fid.write ('] ')
+      fid.write ('</p>\n<h2>Arguments</h2>\n<table class=args>\n')
+
+
+      for A in arg:
+        fid.write ('<tr><td><b>' + A[0] + '</b>')
+        if A[1] == 0 or A[2] == 1:
+          fid.write (' [ ')
+          if A[1] == 0: 
+            fid.write ('optional')
+            if A[2] == 1: fid.write (', ')
+          if A[2] == 1: fid.write ('multiples allowed')
+          fid.write (' ]')
+        fid.write ('</td>\n<td>' + A[5] + '</td></tr>\n')
+      fid.write ('</table>\n')
+  
+      fid.write ('<h2>Options</h2>\n<table class=args>\n')
+      for O in opt:
+        fid.write ('<tr><td nowrap><b>-' + O[0] + '</b>')
+        for A in O[5:]: fid.write (' <i>' + A[0] + '</i>')
+        fid.write ('</td>\n<td>' + O[4])
+        if len(O) > 5:
+          fid.write ('\n<table class=opts>\n')
+          for A in O[5:]:
+            fid.write ('<tr><td><i>' + A[0] + '</i></td>\n<td>' + A[5] + '</td></tr>\n')
+          fid.write ('</table>')
+        fid.write ('</td></tr>\n')
+      fid.write ('</table>\n')
+  
+      cmd_file = os.path.join (cmd_dir, binaries[n] + cpp_suffix)
+      timestamp = mtime (cmd_file)
+      if timestamp > most_recent_timestamp: 
+        most_recent_timestamp = timestamp
+        most_recent_cmd = cmd_file
+      fid.write ('''
 <p class=footer>
 Donald Tournier<br>
 MRtrix version #VERSION#<br>
@@ -664,17 +685,16 @@ Last updated #MTIME#
 </html>
 ''')
 
-    fid.close()
 
 
 
-  fid = open (os.path.join (doc_dir, 'commands', 'index.html'), 'wb')
-  start_html (fid, 'list of MRtrix commands', '../faq', '../index', '../index', '../appendix/index')
-  fid.write ('<table class=cmdindex width=100%>\n')
+  with fopen (os.path.join (doc_dir, 'commands', 'index.html'), 'w') as fid:
+    start_html (fid, 'list of MRtrix commands', '../faq', '../index', '../index', '../appendix/index')
+    fid.write ('<table class=cmdindex width=100%>\n')
 
-  for n in range (0,len(binaries)):
-    fid.write ('<tr><td><a href="' + binaries[n] + '.html">' + binaries[n] + '</a></td><td>' + description[n] + '</td></tr>\n')
-  fid.write ('''</table>
+    for n in range (0,len(binaries)):
+      fid.write ('<tr><td><a href="' + binaries[n] + '.html">' + binaries[n] + '</a></td><td>' + description[n] + '</td></tr>\n')
+    fid.write ('''</table>
 
 <p class=footer>
 Donald Tournier<br>
@@ -684,7 +704,6 @@ MRtrix version #VERSION#<br>
 </body>
 </html>
 ''')
-  fid.close()
 
 
 
@@ -703,9 +722,9 @@ def gen_doc ():
         timestamp = SVN_last_changed (os.path.join (cmd_dir, entry.replace (".html", cpp_suffix)))
       if not timestamp:
         timestamp = '-'
-      with open(fname, 'rb') as fd:
+      with fopen(fname, 'r') as fd:
         contents = fd.read()
-      with open(fname, 'wb') as fd:
+      with fopen(fname, 'w') as fd:
         fd.write (contents
            .replace ('#MTIME#', timestamp)
            .replace ('#VERSION#', major + '.' + minor + '.' + micro))
@@ -730,13 +749,11 @@ def clean_cmd ():
     dirs_to_remove.append (bin_dir)
 
   if len(files_to_remove):
-    print '[RM] ' + ' '.join(files_to_remove)
-    sys.stdout.flush()
+    report ('[RM] ' + ' '.join(files_to_remove) + os.linesep)
     for entry in files_to_remove: os.remove (entry)
 
   if len(dirs_to_remove):
-    print '[RM] ' + ' '.join (dirs_to_remove)
-    sys.stdout.flush()
+    report ('[RM] ' + ' '.join (dirs_to_remove) + os.linesep)
     for entry in dirs_to_remove: os.rmdir (entry)
 
 
@@ -769,11 +786,11 @@ def create_link (source, destination):
   else: src = os.path.relpath (source, os.path.dirname(destination))
   try:
     os.symlink (src, destination)
-  except OSError as (errno, strerror):
-    if errno == 17:
-      if verbose: print 'error creating symbolic link "' + destination + '": ' + strerror
+  except OSError as e:
+    if e.errno == 17:
+      if verbose: report ('error creating symbolic link "' + destination + '": ' + e.strerror + os.linesep)
     else:
-      print 'WARNING: error creating symbolic link: ' + strerror
+      report ('WARNING: error creating symbolic link: ' + e.strerror + os.linesep)
       raise
   
 
@@ -809,10 +826,11 @@ def sort_uninstall_list (uninstall_list):
       sorted_file_list[folder] = []
     sorted_file_list[folder].append (file)
  
-    [ folder, link ] = os.path.split (entry[1])
-    if folder not in sorted_link_list.keys():
-      sorted_link_list[folder] = []
-    sorted_link_list[folder].append (link)
+    if entry[1]:
+      [ folder, link ] = os.path.split (entry[1])
+      if folder not in sorted_link_list.keys():
+        sorted_link_list[folder] = []
+      sorted_link_list[folder].append (link)
  
   return [ sorted_file_list, sorted_link_list ]
     
@@ -822,23 +840,23 @@ def sort_uninstall_list (uninstall_list):
 
 def delete (folder, files):
   if verbose: 
-    print 'deleting from folder "' + folder + '": ' + ' '.join(files)
+    report ('deleting from folder "' + folder + '": ' + ' '.join(files) + os.linesep)
   for entry in files:
     try:
       os.remove (os.path.join (folder, entry))
-    except OSError as ( errno, strerror ):
-      print 'error deleting file "' + os.path.join (folder, entry) + '": ' + strerror
+    except OSError as e:
+      report ('error deleting file "' + os.path.join (folder, entry) + '": ' + e.strerror + os.linesep)
   
   if verbose:
-    print 'attempting to delete folder "' + folder + '"...',
+    report ('attempting to delete folder "' + folder + '"... ')
   try: 
     os.removedirs (folder)
-  except OSError as (errno, strerror):
+  except OSError: 
     if verbose:
-      print 'not empty'
+      report ('not empty' + os.linesep)
   else:
     if verbose: 
-      print 'ok'
+      report ('ok' + os.linesep)
 
 
 ###########################################################################
@@ -861,7 +879,7 @@ if 'uninstall' in targets:
   executables = [ os.path.basename (entry) for entry in default_targets() ]
   path = which (executables)
   if not path:
-    print 'ERROR: could not find executables in path'
+    report ('ERROR: could not find executables in path' + os.linesep)
     exit (1)
   path = path.rstrip (os.path.sep)
 
@@ -870,28 +888,34 @@ if 'uninstall' in targets:
     item = add_to_uninstall_list (path, entry)
     if item: uninstall_list.append (item)
 
-  [ prefix, last ] = os.path.split (path)
+  [ symlink_prefix, last ] = os.path.split (path)
+  [ actual_prefix, last ] = os.path.split (os.path.dirname (uninstall_list[0][0]))
+
   if not last == 'bin':
-    print 'ERROR: executables are expected to reside in a folder named "bin"'
+    report ('ERROR: executables are expected to reside in a folder named "bin"' + os.linesep)
     exit (1)
 
-  for entry in os.listdir (os.path.join (prefix, 'lib')):
-    if entry == libname:
-      item = add_to_uninstall_list (os.path.join (prefix, 'lib'), entry)
-      if item: uninstall_list.append (item)
+  lib_path = None
+  if libname in os.listdir (os.path.join (symlink_prefix, 'lib')):
+    lib_path = os.path.join (symlink_prefix, 'lib')
+  elif libname in os.listdir (os.path.join (actual_prefix, 'lib')):
+    lib_path = os.path.join (os.path.join (actual_prefix, 'lib'))
+  if lib_path:
+    item = add_to_uninstall_list (lib_path, libname)
+    if item: uninstall_list.append (item)
 
   [ sorted_file_list, sorted_link_list ] = sort_uninstall_list (uninstall_list)
 
-  print 'WARNING: about to delete files:'
+  report ('WARNING: about to delete files:' + os.linesep)
   for key in sorted_file_list.keys():
-    print '  from folder "' + key + '": ' + ' '.join(sorted_file_list[key])
+    report ('  from folder "' + key + '": ' + ' '.join(sorted_file_list[key]) + os.linesep)
 
-  print 'WARNING: about to delete symbolic links:'
+  report ('WARNING: about to delete symbolic links:' + os.linesep)
   for key in sorted_link_list.keys():
-    print '  from folder "' + key + '": ' + ' '.join(sorted_link_list[key])
+    report ('  from folder "' + key + '": ' + ' '.join(sorted_link_list[key]) + os.linesep)
 
   if os.path.exists (configfile):
-    print 'WARNING: about to delete configuration file: ' + configfile
+    report ('WARNING: about to delete configuration file: ' + configfile + os.linesep)
 
 
   ans = raw_input ('proceed? y|[N]: ')
@@ -903,11 +927,11 @@ if 'uninstall' in targets:
     if os.path.exists (configfile):
       try:
         os.remove (configfile)
-      except OSError as ( errno, strerror ):
-        print 'error deleting file "' + configfile + '": ' + strerror
+      except OSError as e:
+        report ('error deleting file "' + configfile + '": ' + e.strerror + os.linesep)
     exit (0)
 
-  print 'aborted'
+  report ('aborted' + os.linesep)
   exit(1) 
 
 
@@ -920,28 +944,26 @@ if installto is not None:
     installto = default_installto
   dest_bin = os.path.join (installto, 'bin')
   dest_lib = os.path.join (installto, 'lib')
-  print 'installing executables to "' + dest_bin + '" and dynamic library to "' + dest_lib + '"...',
+  report ('installing executables to "' + dest_bin + '" and dynamic library to "' + dest_lib + '"... ')
   apply_recursive (install, bin_dir, dest_bin)
   apply_recursive (install, os.path.join (lib_dir, libname), os.path.join (dest_lib, libname))
-  print 'ok'
+  report ('ok' + os.linesep)
 
   if linkto is not None:
     if len (linkto) == 0:
       linkto = default_linkto
     link_bin = os.path.join (linkto, 'bin')
-    link_lib = os.path.join (linkto, 'lib')
-    print 'creating symbolic links to executables in "' + link_bin + '" and to dynamic library in "' + link_lib + '"...',
+    report ('creating symbolic links to executables in "' + link_bin + '"... ')
     apply_recursive (create_link, dest_bin, link_bin)
-    apply_recursive (create_link, os.path.join (dest_lib, libname), os.path.join (link_lib, libname))
-    print 'ok'
+    report ('ok' + os.linesep)
 
   if not os.path.exists (configfile):
-    print 'creating default system-wide configuration file (setting up multi-threading with ' + str(num_processors) + ' threads)...',
-    with open(configfile, 'wb') as fd:
+    report ('creating default system-wide configuration file (setting up multi-threading with ' + str(num_processors) + ' threads)... ')
+    with fopen(configfile, 'wb') as fd:
       fd.write ('NumberOfThreads: ' + str(num_processors) + '\n');
-    print 'ok'
+    report ('ok' + os.linesep)
   else:
-    print 'configuration file already exists - leaving as-is'
+    report ('configuration file already exists - leaving as-is' + os.linesep)
 
   exit(0)
 
@@ -967,48 +989,52 @@ if doc_dir in targets:
 if len(targets) == 0: targets = default_targets()
 
 if verbose:
-  print ''
-  print 'building targets:',
+  report ('''
+building targets: ''')
   for entry in targets:
-    print entry,
-  print ''
-  sys.stdout.flush()
+    report (entry + ' ')
+  report (os.linesep + os.linesep)
 
 if verbose:
-  print '' 
-  print 'compiling TODO list...'
+  report (''' 
+compiling TODO list... ''')
 [ Entry(item) for item in targets ]
-for item in todo.keys():
-  if todo[item].action == 'NA' or (todo[item].timestamp and todo[item].timestamp >= todo[item].dep_timestamp):
-    del todo[item]
+todo_tmp = todo
+todo = {}
+for item in todo_tmp.keys():
+  if not ( todo_tmp[item].action == 'NA' or (todo_tmp[item].timestamp and todo_tmp[item].timestamp >= todo_tmp[item].dep_timestamp) ):
+    todo[item] = todo_tmp[item]
 if verbose: 
-  print 'TODO list contains ' + str(len(todo)) + ' items'
-  print ''
+  report ('TODO list contains ' + str(len(todo)) + ''' items
+
+''')
 
 #for entry in todo.values(): entry.display()
 
 if dependencies:
-  print '======================================='
-  print '  Objects'
-  print '======================================='
+  report ('''=======================================
+  Objects
+=======================================''')
   for entry in object_deps.keys():
-    print entry + ' [' + str(file_flags[entry]) + ']:'
-    for item in object_deps[entry]: print '  ' + item 
+    report (entry + ' [' + str(file_flags[entry]) + ']:')
+    for item in object_deps[entry]: 
+      report ('  ' + item + os.linesep)
 
-  print '======================================='
-  print '  Headers'
-  print '======================================='
+  report ('''=======================================
+  Headers
+=======================================''')
   for entry in headers.keys():
-    print entry + ' [' + str(file_flags[entry]) + ']:'
-    for item in headers[entry]: print '  ' + item + ' [' + str(file_flags[item]) + ']'
+    report (entry + ' [' + str(file_flags[entry]) + ']:' + os.linesep)
+    for item in headers[entry]: 
+      report ('  ' + item + ' [' + str(file_flags[item]) + ']' + os.linesep)
 
 
 
 
 if verbose:
-  print '' 
-  print 'launching ' + str(num_processors) + ' threads'
-  print '' 
+  report (''' 
+launching ''' + str(num_processors) + ''' threads
+''')
 
 threads = []
 for i in range (1, num_processors):
diff --git a/cmd/cat_tracks.cpp b/cmd/cat_tracks.cpp
new file mode 100644
index 0000000..40faa4f
--- /dev/null
+++ b/cmd/cat_tracks.cpp
@@ -0,0 +1,94 @@
+/*
+    Copyright 2008 Brain Research Institute, Melbourne, Australia
+
+    Written by Robert E. Smith and J-Donald Tournier, 14/09/11.
+
+    This file is part of MRtrix.
+
+    MRtrix is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    MRtrix is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "app.h"
+#include "image/interp.h"
+#include "math/vector.h"
+#include "point.h"
+#include "dwi/tractography/file.h"
+#include "dwi/tractography/roi.h"
+#include "dwi/tractography/tracker/base.h"
+
+
+using namespace std; 
+using namespace MR; 
+using namespace MR::DWI; 
+using namespace MR::DWI::Tractography; 
+
+SET_VERSION_DEFAULT;
+
+DESCRIPTION = {
+  "Concatenate two track files",
+  NULL
+};
+
+ARGUMENTS = {
+  Argument ("output", "output tracks file", "the output file containing the tracks.")     .type_file(),
+  Argument ("track1", "first input tracks file",  "the first input tracks file.").type_file(),
+  Argument ("track2", "second tracks file", "the second input tracks file.", true, true)     .type_file(),
+  Argument::End
+};
+
+
+OPTIONS = {
+
+  Option::End
+};
+
+EXECUTE
+{
+  	int num_tracks = argument.size()-1;
+	Reader reader;
+	Properties properties;
+	Writer writer;
+	Properties outputproperties;
+
+	reader.open (argument[1].get_string(), properties);
+
+	//const float progress_multiplier = properties["count"].empty() ? 0.0 : 100.0 / to<float> (properties["count"]);
+	
+	outputproperties = Properties(properties);
+
+	outputproperties["count"] = properties["count"];
+	outputproperties["total_count"] = properties["total_count"];
+
+	//properties.erase ("count");
+	//properties.erase ("total_count");
+	outputproperties["source"] = argument[0].get_string();
+	
+	writer.create (argument[0].get_string(), outputproperties);
+
+	reader.close();
+	for(int i = 1; i <= num_tracks; i++)
+	{
+		reader.open (argument[i].get_string(), properties);
+		
+		std::vector<Point> tck;
+		while (reader.next (tck))
+		{
+			++writer.total_count;
+			writer.append (tck);
+		}
+		reader.close();
+	}	
+	writer.close();
+}
diff --git a/cmd/csdeconv.cpp b/cmd/csdeconv.cpp
index e43b21e..93a1897 100644
--- a/cmd/csdeconv.cpp
+++ b/cmd/csdeconv.cpp
@@ -653,7 +653,7 @@ EXECUTE {
   info ("launching " + str (num_threads) + " threads");
 
   ProgressBar::init (dwi.dim(0)*dwi.dim(1)*dwi.dim(2), "performing constrained spherical deconvolution...");
-  Glib::Thread* threads[num_threads-1];
+  std::vector<Glib::Thread*> threads (num_threads-1);
   for (int n = 0; n < num_threads-1; n++) 
     threads[n] = Glib::Thread::create (sigc::mem_fun (threader, &Thread::execute), true);
 
diff --git a/cmd/disp_profile.cpp b/cmd/disp_profile.cpp
index 1a5a91c..5ccc83e 100644
--- a/cmd/disp_profile.cpp
+++ b/cmd/disp_profile.cpp
@@ -33,7 +33,7 @@
     * set lmax based on SH values provided
     
 */
-
+#include <glibmm.h>
 #include <gtk/gtkgl.h>
 #include <gtkmm/main.h>
 #include <gtkmm/window.h>
diff --git a/cmd/dwi2tensor.cpp b/cmd/dwi2tensor.cpp
index eaac2bf..d99354e 100644
--- a/cmd/dwi2tensor.cpp
+++ b/cmd/dwi2tensor.cpp
@@ -131,7 +131,7 @@ EXECUTE {
 
   ProgressBar::init (dwi.dim(0)*dwi.dim(1)*dwi.dim(2), "converting DW images to tensor image..."); 
 
-  float d[dwi.dim(axis)];
+  std::vector<float> d (dwi.dim(axis));
   for (dwi.set(2,0), dt.set(2,0); dwi[2] < dwi.dim(2); dwi.inc(2), dt.inc(2)) {
 
     grad.copy (bmat);
diff --git a/cmd/estimate_response.cpp b/cmd/estimate_response.cpp
index 9ab3c50..d1c7ccb 100644
--- a/cmd/estimate_response.cpp
+++ b/cmd/estimate_response.cpp
@@ -149,6 +149,7 @@ EXECUTE {
 
   response.zero();
 
+  std::vector<float> val (dwi.dim(3));
   ProgressBar::init (count, "estimating response function...");
 
   for (dwi.set(2,0), mask.set(2,0); dwi[2] < dwi.dim(2); dwi.inc(2), mask.inc(2)) {
@@ -157,7 +158,6 @@ EXECUTE {
 
 	if (mask.value() > 0.5) {
 
-          float val [dwi.dim(3)];
           for (dwi.set(3,0); dwi[3] < dwi.dim(3); dwi.inc(3)) {
 	    val[dwi[3]] = dwi.value();
             val[dwi[3]] = val[dwi[3]] > 0.0 ? -log (val[dwi[3]]) : -1e-12;
diff --git a/cmd/find_SH_peaks.cpp b/cmd/find_SH_peaks.cpp
index ddb8b21..f64a9c8 100644
--- a/cmd/find_SH_peaks.cpp
+++ b/cmd/find_SH_peaks.cpp
@@ -121,7 +121,7 @@ EXECUTE {
   Image::Position out (*argument[2].get_image (header));
 
   std::vector<Direction> peaks_out (npeaks);
-  float val[SH.dim(3)];
+  std::vector<float> val (SH.dim(3));
   int lmax = DWI::SH::LforN (SH.dim(3));
   DWI::SH::precompute (lmax, 512);
   
@@ -163,12 +163,12 @@ EXECUTE {
           std::vector<Direction> all_peaks;
           for (guint i = 0; i < dirs.rows(); i++) {
             Direction p (dirs(i,0), dirs(i,1)); 
-            p.a = DWI::SH::get_peak (val, lmax, p.v, true);
+            p.a = DWI::SH::get_peak (&val[0], lmax, p.v, true);
             
             if (gsl_finite (p.a)) {
               for (guint j = 0; j < all_peaks.size(); j++) {
                 if (fabs (p.v.dot (all_peaks[j].v)) > DOT_THRESHOLD) {
-                  p.a = NAN;
+                  p.a = GSL_NAN;
                   break;
                 }
               }
diff --git a/cmd/gen_ROI.cpp b/cmd/gen_ROI.cpp
index 5fee812..358e97f 100644
--- a/cmd/gen_ROI.cpp
+++ b/cmd/gen_ROI.cpp
@@ -86,7 +86,7 @@ EXECUTE {
   header.offset = 0.0;
   header.scale = 1.0;
 
-  std::vector<int> pos[in_obj.ndim()];
+  std::vector< std::vector<int> > pos (in_obj.ndim());
 
   std::vector<OptBase> opt = get_options (0); // coord
   for (guint n = 0; n < opt.size(); n++) {
diff --git a/cmd/median3D.cpp b/cmd/median3D.cpp
index b7a2b62..fcf3b39 100644
--- a/cmd/median3D.cpp
+++ b/cmd/median3D.cpp
@@ -73,7 +73,7 @@ EXECUTE {
           avg = (n+1)%2;
           n = (n/2)+1;
           nc = 0;
-          cm = -INFINITY;
+          cm = GSL_NEGINF;
 
           for (in.set(2,from[2]); in[2] < to[2]; in.inc(2)) {
             for (in.set(1,from[1]); in[1] < to[1]; in.inc(1)) {
@@ -87,7 +87,7 @@ EXECUTE {
                 else if (val < cm) {
                   for (i = 0; v[i] != cm; i++);
                   v[i] = val;
-                  cm = -INFINITY;
+                  cm = GSL_NEGINF;
                   for (i = 0; i < n; i++)
                     if (v[i] > cm) cm = v[i];
                 }
@@ -96,7 +96,7 @@ EXECUTE {
           }
 
           if (avg) {
-            t = cm = -INFINITY;
+            t = cm = GSL_NEGINF;
             for (i = 0; i < n; i++) {
               if (v[i] > cm) {
                 t = cm;
diff --git a/cmd/mradd.cpp b/cmd/mradd.cpp
index 60170e9..f70dea5 100644
--- a/cmd/mradd.cpp
+++ b/cmd/mradd.cpp
@@ -53,7 +53,7 @@ OPTIONS = { Option::End };
 EXECUTE {
   guint num_images = argument.size()-1;
 
-  RefPtr<Image::Object> in[num_images];
+  std::vector< RefPtr<Image::Object> > in (num_images);
   in[0] = argument[0].get_image();
   Image::Header header (in[0]->header());
 
diff --git a/cmd/mrcat.cpp b/cmd/mrcat.cpp
index bf319e4..4169a7d 100644
--- a/cmd/mrcat.cpp
+++ b/cmd/mrcat.cpp
@@ -57,7 +57,7 @@ EXECUTE {
   if (opt.size()) axis = opt[0][0].get_int();
 
   int num_images = argument.size()-1;
-  RefPtr<Image::Object> in[num_images];
+  std::vector< RefPtr<Image::Object> > in (num_images);
   in[0] = argument[0].get_image();
   Image::Header header (in[0]->header());
 
diff --git a/cmd/mrconvert.cpp b/cmd/mrconvert.cpp
index d901bac..dea4a69 100644
--- a/cmd/mrconvert.cpp
+++ b/cmd/mrconvert.cpp
@@ -75,10 +75,10 @@ OPTIONS = {
     .append (Argument ("spec", "specifier", "the data type specifier.").type_choice (data_type_choices)),
 
   Option ("scale", "scaling factor", "apply scaling to the intensity values.")
-    .append (Argument ("factor", "factor", "the factor by which to multiply the intensities.").type_float (NAN, NAN, 1.0)),
+    .append (Argument ("factor", "factor", "the factor by which to multiply the intensities.").type_float (GSL_NAN, GSL_NAN, 1.0)),
 
   Option ("offset", "offset", "apply offset to the intensity values.")
-    .append (Argument ("bias", "bias", "the value of the offset.").type_float (NAN, NAN, 0.0)),
+    .append (Argument ("bias", "bias", "the value of the offset.").type_float (GSL_NAN, GSL_NAN, 0.0)),
 
   Option ("zero", "replace NaN by zero", "replace all NaN values with zero."),
 
@@ -91,12 +91,17 @@ OPTIONS = {
 
   Option ("prs", "DW gradient specified as PRS", "assume that the DW gradients are specified in the PRS frame (Siemens DICOM only)."),
 
+  Option ("nocomment", "remove comments", "remove all comments from the header. Useful to anonymise data."),
+
+  Option ("addcomment", "add comments", "add a new comment to the header.", false, true)
+    .append (Argument ("comment", "comment", "the text to add as a comment.").type_string()),
+
   Option::End
 };
 
 
 
-inline bool next (Image::Position& ref, Image::Position& other, const std::vector<int>* pos)
+inline bool next (Image::Position& ref, Image::Position& other, const std::vector< std::vector<int> >& pos)
 {
   int axis = 0;
   do {
@@ -167,7 +172,7 @@ EXECUTE {
   if (opt.size()) header.data_type.parse (data_type_choices[opt[0][0].get_int()]);
 
   for (guint n = 0; n < vox.size(); n++) 
-    if (isfinite (vox[n])) header.axes.vox[n] = vox[n];
+    if (gsl_finite (vox[n])) header.axes.vox[n] = vox[n];
 
   opt = get_options (7); // layout
   if (opt.size()) {
@@ -192,7 +197,16 @@ EXECUTE {
     }
   }
 
-  std::vector<int> pos[in_obj.ndim()];
+  opt = get_options (9); // nocomment
+  if (opt.size()) 
+    header.comments.clear();
+
+  opt = get_options (10); // addcomment
+  for (guint n = 0; n < opt.size(); ++n) 
+    header.comments.push_back (opt[n][0].get_string());
+
+
+  std::vector< std::vector<int> > pos (in_obj.ndim());
 
   opt = get_options (0); // coord
   for (guint n = 0; n < opt.size(); n++) {
diff --git a/cmd/mrstats.cpp b/cmd/mrstats.cpp
index 32f1eb0..3c879ee 100644
--- a/cmd/mrstats.cpp
+++ b/cmd/mrstats.cpp
@@ -79,7 +79,7 @@ template <class Function> inline void loop (Image::Position& pos, RefPtr<Image::
         if (mask) if (mask->value() < 0.5) skip = true;
         if (!skip) {
           float val = pos.value();
-          if (isfinite (val)) func (val);
+          if (gsl_finite (val)) func (val);
         }
         if (mask) mask->inc (0);
       }
@@ -92,7 +92,7 @@ template <class Function> inline void loop (Image::Position& pos, RefPtr<Image::
 
 class GetStats {
   public:
-    GetStats () : mean (0.0), std (0.0), min (INFINITY), max (-INFINITY), count (0) { }
+    GetStats () : mean (0.0), std (0.0), min (GSL_POSINF), max (GSL_NEGINF), count (0) { }
 
     double mean, std;
     float min, max;
@@ -117,7 +117,7 @@ class GetStats {
 
 class GetMinMax {
   public:
-    GetMinMax () : min (INFINITY), max (-INFINITY) { }
+    GetMinMax () : min (GSL_POSINF), max (GSL_NEGINF) { }
 
     float min, max;
 
diff --git a/cmd/mrtransform.cpp b/cmd/mrtransform.cpp
index d09d78b..2862667 100644
--- a/cmd/mrtransform.cpp
+++ b/cmd/mrtransform.cpp
@@ -147,9 +147,9 @@ EXECUTE {
     opt = get_options (6); // upsample
     if (opt.size()) {
       float factor = opt[0][0].get_float();
-      header.axes.dim[0] = round (header.axes.dim[0] * factor);
-      header.axes.dim[1] = round (header.axes.dim[1] * factor);
-      header.axes.dim[2] = round (header.axes.dim[2] * factor);
+      header.axes.dim[0] = MR::round (header.axes.dim[0] * factor);
+      header.axes.dim[1] = MR::round (header.axes.dim[1] * factor);
+      header.axes.dim[2] = MR::round (header.axes.dim[2] * factor);
       header.axes.vox[0] /= factor;
       header.axes.vox[1] /= factor;
       header.axes.vox[2] /= factor;
diff --git a/cmd/mrview.cpp b/cmd/mrview.cpp
index 3be322d..86f354e 100644
--- a/cmd/mrview.cpp
+++ b/cmd/mrview.cpp
@@ -20,6 +20,7 @@
 
 */
 
+#include <glibmm.h>
 #include <gtkmm/main.h>
 #include <gtk/gtkgl.h>
 
diff --git a/cmd/streamtrack.cpp b/cmd/streamtrack.cpp
index fe238a6..1b08ed7 100644
--- a/cmd/streamtrack.cpp
+++ b/cmd/streamtrack.cpp
@@ -259,7 +259,7 @@ class Threader {
         max_num_attempts = to<guint> (properties["max_num_attempts"]);
 
       unidirectional = to<int> (properties["unidirectional"]);
-      min_size = round (to<float> (properties["min_dist"]) / to<float> (properties["step_size"]));
+      min_size = MR::round (to<float> (properties["min_dist"]) / to<float> (properties["step_size"]));
 
       writer.create (output_file, properties);
     }
@@ -271,7 +271,7 @@ class Threader {
       currently_running = num_threads;
       guint rng_seed = time (NULL);
 
-      Glib::Thread* threads[num_threads];
+      std::vector<Glib::Thread*> threads (num_threads);
       for (int n = 0; n < num_threads; n++) {
         trackers[n]->set_rng_seed (rng_seed + n);
         threads[n] = Glib::Thread::create (sigc::bind<Tracker::Base*> (sigc::mem_fun (*this, &Threader::execute), trackers[n]), true);
@@ -329,14 +329,16 @@ class Threader {
           if (writer.count < max_num_tracks) {
             writer.append (*tck);
             writer.total_count++;
-            fprintf (stderr, "\r%8u generated, %8u selected    [%3d%%]", 
-                writer.total_count, writer.count, (int) ((100.0*writer.count)/(float) max_num_tracks));
+            if (App::log_level) 
+              fprintf (stderr, "\r%8u generated, %8u selected    [%3d%%]", 
+                  writer.total_count, writer.count, (int) ((100.0*writer.count)/(float) max_num_tracks));
           }
           delete tck;
         }
       } while (currently_running > 0);
 
-      fprintf (stderr, "\r%8u generated, %8u selected    [100%%]\n", writer.total_count, writer.count);
+      if (App::log_level) 
+        fprintf (stderr, "\r%8u generated, %8u selected    [100%%]\n", writer.total_count, writer.count);
       writer.close ();
     }
 
diff --git a/cmd/threshold.cpp b/cmd/threshold.cpp
index 198d0d3..47216d2 100644
--- a/cmd/threshold.cpp
+++ b/cmd/threshold.cpp
@@ -52,10 +52,10 @@ ARGUMENTS = {
 
 OPTIONS = { 
   Option ("abs", "absolute threshold", "specify threshold value as absolute intensity.")
-    .append (Argument ("value", "value", "the absolute threshold to use.").type_float (NAN, NAN, 0.0)),
+    .append (Argument ("value", "value", "the absolute threshold to use.").type_float (GSL_NAN, GSL_NAN, 0.0)),
 
   Option ("percent", "percentage threshold", "specify threshold value as a percentage of the peak intensity in the input image.")
-    .append (Argument ("value", "value", "the percentage threshold to use.").type_float (NAN, NAN, 0.0)),
+    .append (Argument ("value", "value", "the percentage threshold to use.").type_float (GSL_NAN, GSL_NAN, 0.0)),
 
   Option ("nonbinary", "non-binary", "output image retains original image intensities above the threshold (for a non-binary image output)"),
 
@@ -70,7 +70,7 @@ OPTIONS = {
 EXECUTE {
 
   bool use_percentage = false, optimise = true;
-  float val = NAN;
+  float val = GSL_NAN;
 
   std::vector<OptBase> opt = get_options (0);
   if (opt.size()) {
@@ -115,7 +115,7 @@ EXECUTE {
     val = hist.first_min();
   }
 
-  float zero = use_NaN ? NAN : 0.0;
+  float zero = use_NaN ? GSL_NAN : 0.0;
   float one  = invert ? zero : 1.0;
   zero = invert ? 1.0 : zero;
 
diff --git a/cmd/tracks2prob.cpp b/cmd/tracks2prob.cpp
index c744e09..50101ca 100644
--- a/cmd/tracks2prob.cpp
+++ b/cmd/tracks2prob.cpp
@@ -131,7 +131,7 @@ class Voxel
       y (round (p[1])),
       z (round (p[2]))
     { 
-      assert (finite (p[0]) && finite (p[1]) && finite (p[2]));
+      assert (gsl_finite (p[0]) && gsl_finite (p[1]) && gsl_finite (p[2]));
     }
 
     Voxel& operator+= (const Voxel& source) 
@@ -545,8 +545,8 @@ void generate_header (Image::Header& header, DWI::Tractography::Reader& file, co
   std::vector<Point> tck;
   size_t track_counter = 0;
 
-  Point min_values ( INFINITY,  INFINITY,  INFINITY);
-  Point max_values (-INFINITY, -INFINITY, -INFINITY);
+  Point min_values (GSL_POSINF, GSL_POSINF, GSL_POSINF);
+  Point max_values (GSL_NEGINF, GSL_NEGINF, GSL_NEGINF);
 
   ProgressBar::init (0, "creating new template image... ");
 
diff --git a/doc/appendix/config.html b/doc/appendix/config.html
index 9c3ef1b..b93e5dd 100644
--- a/doc/appendix/config.html
+++ b/doc/appendix/config.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -89,8 +89,8 @@ These parameters are currently interpreted by MRtrix:
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/appendix/index.html b/doc/appendix/index.html
index 57a0706..183b03b 100644
--- a/doc/appendix/index.html
+++ b/doc/appendix/index.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -26,8 +26,8 @@
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/appendix/mrtrix.html b/doc/appendix/mrtrix.html
index 403fd82..a82a963 100644
--- a/doc/appendix/mrtrix.html
+++ b/doc/appendix/mrtrix.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -169,8 +169,8 @@ triplet of Inf values is used to indicate the end of the file.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/appendix/refs.html b/doc/appendix/refs.html
index 9693587..66ee7d0 100644
--- a/doc/appendix/refs.html
+++ b/doc/appendix/refs.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -49,8 +49,8 @@
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/commands/average.html b/doc/commands/average.html
index 85104c3..7c492ff 100644
--- a/doc/commands/average.html
+++ b/doc/commands/average.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -14,7 +14,7 @@
     <td><a href="index.html"><img src="../up.png"></a></td>
     <td><a href="../index.html"><img src="../home.png"></a></td>
     <th>average</th>
-    <td><a href="cleanup_ANTS_warp.html"><img src="../right.png"></a></td>
+    <td><a href="cat_tracks.html"><img src="../right.png"></a></td>
   </tr>
 </table>
 
@@ -54,8 +54,8 @@ average an image along a specific axis.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mradd.html b/doc/commands/cat_tracks.html
similarity index 62%
copy from doc/commands/mradd.html
copy to doc/commands/cat_tracks.html
index bf07035..bbdf38e 100644
--- a/doc/commands/mradd.html
+++ b/doc/commands/cat_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -10,27 +10,27 @@
 
 <table class=nav>
   <tr>
-    <td><a href="mrabs.html"><img src="../left.png"></a></td>
+    <td><a href="average.html"><img src="../left.png"></a></td>
     <td><a href="index.html"><img src="../up.png"></a></td>
     <td><a href="../index.html"><img src="../home.png"></a></td>
-    <th>mradd</th>
-    <td><a href="mrcat.html"><img src="../right.png"></a></td>
+    <th>cat_tracks</th>
+    <td><a href="cleanup_ANTS_warp.html"><img src="../right.png"></a></td>
   </tr>
 </table>
 
 <h2>Description</h2>
 <p>
-add or subtract images
+Concatenate two track files
 </p>
-<p class=indented><strong>syntax:</strong>     mradd [ options ] image1 image2 [ image2 ... ] output </p>
+<p class=indented><strong>syntax:</strong>     cat_tracks [ options ] output track1 track2 [ track2 ... ] </p>
 <h2>Arguments</h2>
 <table class=args>
-<tr><td><b>image1</b></td>
-<td>the first input image.</td></tr>
-<tr><td><b>image2</b> [ multiples allowed ]</td>
-<td>the second input image.</td></tr>
 <tr><td><b>output</b></td>
-<td>the output image.</td></tr>
+<td>the output file containing the tracks.</td></tr>
+<tr><td><b>track1</b></td>
+<td>the first input tracks file.</td></tr>
+<tr><td><b>track2</b> [ multiples allowed ]</td>
+<td>the second input tracks file.</td></tr>
 </table>
 <h2>Options</h2>
 <table class=args>
@@ -48,8 +48,8 @@ add or subtract images
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/cleanup_ANTS_warp.html b/doc/commands/cleanup_ANTS_warp.html
index d0e34d9..6f73281 100644
--- a/doc/commands/cleanup_ANTS_warp.html
+++ b/doc/commands/cleanup_ANTS_warp.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -10,7 +10,7 @@
 
 <table class=nav>
   <tr>
-    <td><a href="average.html"><img src="../left.png"></a></td>
+    <td><a href="cat_tracks.html"><img src="../left.png"></a></td>
     <td><a href="index.html"><img src="../up.png"></a></td>
     <td><a href="../index.html"><img src="../home.png"></a></td>
     <th>cleanup_ANTS_warp</th>
@@ -48,8 +48,8 @@ clean up warp field generated by ANTS (or more specifically, WarpMultiImageTrans
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/csdeconv.html b/doc/commands/csdeconv.html
index 6c9b9ac..70ce8c0 100644
--- a/doc/commands/csdeconv.html
+++ b/doc/commands/csdeconv.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -143,8 +143,8 @@ etc...
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/dicom_filename.html b/doc/commands/dicom_filename.html
index ee7ec6c..fab57f0 100644
--- a/doc/commands/dicom_filename.html
+++ b/doc/commands/dicom_filename.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -44,8 +44,8 @@ read a DICOM file and output a suitable filename for its storage.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/dir2amp.html b/doc/commands/dir2amp.html
index 6374a29..50ada98 100644
--- a/doc/commands/dir2amp.html
+++ b/doc/commands/dir2amp.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ convert directions image to amplitudes.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/disp_profile.html b/doc/commands/disp_profile.html
index 69e334e..5e2fcce 100644
--- a/doc/commands/disp_profile.html
+++ b/doc/commands/disp_profile.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ view spherical harmonics surface plots.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/dwi2SH.html b/doc/commands/dwi2SH.html
index 26a3c6f..8ea5c43 100644
--- a/doc/commands/dwi2SH.html
+++ b/doc/commands/dwi2SH.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -108,8 +108,8 @@ etc...
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/dwi2tensor.html b/doc/commands/dwi2tensor.html
index 97cc8c9..0369979 100644
--- a/doc/commands/dwi2tensor.html
+++ b/doc/commands/dwi2tensor.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -66,8 +66,8 @@ convert diffusion-weighted images to tensor images.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/erode.html b/doc/commands/erode.html
index ae397be..6bb0c53 100644
--- a/doc/commands/erode.html
+++ b/doc/commands/erode.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -54,8 +54,8 @@ erode (or dilate) mask (i.e. binary) image
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/estimate_response.html b/doc/commands/estimate_response.html
index a5eb313..d38aafc 100644
--- a/doc/commands/estimate_response.html
+++ b/doc/commands/estimate_response.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -62,8 +62,8 @@ estimate the fibre response function for use in spherical deconvolution.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/filter_tracks.html b/doc/commands/filter_tracks.html
index 3374694..a47d2eb 100644
--- a/doc/commands/filter_tracks.html
+++ b/doc/commands/filter_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -74,8 +74,8 @@ Each region-of-interest should be either the path to a binary mask image, or a c
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/find_SH_peaks.html b/doc/commands/find_SH_peaks.html
index f6a93dd..02869be 100644
--- a/doc/commands/find_SH_peaks.html
+++ b/doc/commands/find_SH_peaks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -74,8 +74,8 @@ identify the orientations of the N largest peaks of a SH profile
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/gen_ROI.html b/doc/commands/gen_ROI.html
index ae128f1..d7317a4 100644
--- a/doc/commands/gen_ROI.html
+++ b/doc/commands/gen_ROI.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -60,8 +60,8 @@ generate a mask ROI with the same dimensions as the input dataset, and all voxel
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/gen_WM_mask.html b/doc/commands/gen_WM_mask.html
index f1e8701..20a8690 100644
--- a/doc/commands/gen_WM_mask.html
+++ b/doc/commands/gen_WM_mask.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -60,8 +60,8 @@ generate a white matter probability mask from the DW images.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/gen_unit_warp.html b/doc/commands/gen_unit_warp.html
index f867d68..8c7140e 100644
--- a/doc/commands/gen_unit_warp.html
+++ b/doc/commands/gen_unit_warp.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ generate a warp field corresponding to a no-warp operation. This is useful to ob
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/gendir.html b/doc/commands/gendir.html
index cd1147e..087d576 100644
--- a/doc/commands/gendir.html
+++ b/doc/commands/gendir.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -58,8 +58,8 @@ generate a set of directions evenly distributed over a hemisphere.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/import_tracks.html b/doc/commands/import_tracks.html
index dc17a41..a39679d 100644
--- a/doc/commands/import_tracks.html
+++ b/doc/commands/import_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -49,8 +49,8 @@ The input ascii files should consist of 3xN matrices, corresponding to the [ X Y
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/index.html b/doc/commands/index.html
index 4c9a439..e875d48 100644
--- a/doc/commands/index.html
+++ b/doc/commands/index.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -20,6 +20,7 @@
 
 <table class=cmdindex width=100%>
 <tr><td><a href="average.html">average</a></td><td>average an image along a specific axis.</td></tr>
+<tr><td><a href="cat_tracks.html">cat_tracks</a></td><td>Concatenate two track files</td></tr>
 <tr><td><a href="cleanup_ANTS_warp.html">cleanup_ANTS_warp</a></td><td>clean up warp field generated by ANTS (or more specifically, WarpMultiImageTransform, replacing background voxels with NaN when they map to regions outside the original image.</td></tr>
 <tr><td><a href="csdeconv.html">csdeconv</a></td><td>perform non-negativity constrained spherical deconvolution.</td></tr>
 <tr><td><a href="dicom_filename.html">dicom_filename</a></td><td>read a DICOM file and output a suitable filename for its storage.</td></tr>
@@ -67,7 +68,7 @@
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
+MRtrix version 0.2.12<br>
 </p>
 
 </body>
diff --git a/doc/commands/median3D.html b/doc/commands/median3D.html
index 7c9d592..eac8f7d 100644
--- a/doc/commands/median3D.html
+++ b/doc/commands/median3D.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ smooth images using a 3x3x3 median filter.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrabs.html b/doc/commands/mrabs.html
index e5c648d..cafd6a6 100644
--- a/doc/commands/mrabs.html
+++ b/doc/commands/mrabs.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ take absolute value of image intensity
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mradd.html b/doc/commands/mradd.html
index bf07035..ae1d506 100644
--- a/doc/commands/mradd.html
+++ b/doc/commands/mradd.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -48,8 +48,8 @@ add or subtract images
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrcat.html b/doc/commands/mrcat.html
index c4144a4..baf3425 100644
--- a/doc/commands/mrcat.html
+++ b/doc/commands/mrcat.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -54,8 +54,8 @@ concatenate several images into one
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrconvert.html b/doc/commands/mrconvert.html
index ecf5dd4..97c40e1 100644
--- a/doc/commands/mrconvert.html
+++ b/doc/commands/mrconvert.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -83,6 +83,14 @@ If used correctly, this program can be a very useful workhorse. In addition to c
 </table></td></tr>
 <tr><td nowrap><b>-prs</b></td>
 <td>assume that the DW gradients are specified in the PRS frame (Siemens DICOM only).</td></tr>
+<tr><td nowrap><b>-nocomment</b></td>
+<td>remove all comments from the header. Useful to anonymise data.</td></tr>
+<tr><td nowrap><b>-addcomment</b> <i>comment</i></td>
+<td>add a new comment to the header.
+<table class=opts>
+<tr><td><i>comment</i></td>
+<td>the text to add as a comment.</td></tr>
+</table></td></tr>
 <tr><td nowrap><b>-info</b></td>
 <td>display information messages.</td></tr>
 <tr><td nowrap><b>-quiet</b></td>
@@ -97,8 +105,8 @@ If used correctly, this program can be a very useful workhorse. In addition to c
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrinfo.html b/doc/commands/mrinfo.html
index 0ae2e1e..9c7903f 100644
--- a/doc/commands/mrinfo.html
+++ b/doc/commands/mrinfo.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -50,8 +50,8 @@ display header information
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrmult.html b/doc/commands/mrmult.html
index 3c9a6cd..64336d3 100644
--- a/doc/commands/mrmult.html
+++ b/doc/commands/mrmult.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ multiply images
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrstats.html b/doc/commands/mrstats.html
index 1e5c0bd..c6857e0 100644
--- a/doc/commands/mrstats.html
+++ b/doc/commands/mrstats.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -68,8 +68,8 @@ compute images statistics.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrtransform.html b/doc/commands/mrtransform.html
index 29429cc..ccef3c2 100644
--- a/doc/commands/mrtransform.html
+++ b/doc/commands/mrtransform.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -76,8 +76,8 @@ apply spatial transformations or reslice images.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/mrview.html b/doc/commands/mrview.html
index aaf81b9..94d19e0 100644
--- a/doc/commands/mrview.html
+++ b/doc/commands/mrview.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -44,8 +44,8 @@ the MRtrix image viewer.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/normalise_tracks.html b/doc/commands/normalise_tracks.html
index e631eb5..6b28895 100644
--- a/doc/commands/normalise_tracks.html
+++ b/doc/commands/normalise_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -48,8 +48,8 @@ apply a normalisation map to a tracks file.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/read_dicom.html b/doc/commands/read_dicom.html
index 4bf136e..eea8e88 100644
--- a/doc/commands/read_dicom.html
+++ b/doc/commands/read_dicom.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -48,8 +48,8 @@ output DICOM fields in human-readable format.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/read_ximg.html b/doc/commands/read_ximg.html
index 7f59c11..6c33f34 100644
--- a/doc/commands/read_ximg.html
+++ b/doc/commands/read_ximg.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -44,8 +44,8 @@ output XIMG fields in human-readable format.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/resample_tracks.html b/doc/commands/resample_tracks.html
index 8f7fe36..3a7c021 100644
--- a/doc/commands/resample_tracks.html
+++ b/doc/commands/resample_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -83,8 +83,8 @@ Use the -extent option to specify the range within which the resampling should t
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/sample_tracks.html b/doc/commands/sample_tracks.html
index 6672546..73a0654 100644
--- a/doc/commands/sample_tracks.html
+++ b/doc/commands/sample_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -51,8 +51,8 @@ the track file should generally have been produced by resample_tracks to ensure
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/sdeconv.html b/doc/commands/sdeconv.html
index 5d129c6..237c807 100644
--- a/doc/commands/sdeconv.html
+++ b/doc/commands/sdeconv.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -119,8 +119,8 @@ etc...
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/select_tracks.html b/doc/commands/select_tracks.html
index 5a55b7f..6804626 100644
--- a/doc/commands/select_tracks.html
+++ b/doc/commands/select_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -55,8 +55,8 @@ Note that the track properties (as listed in the header of the file, and display
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/streamtrack.html b/doc/commands/streamtrack.html
index fe81aaa..fb681a8 100644
--- a/doc/commands/streamtrack.html
+++ b/doc/commands/streamtrack.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -146,8 +146,8 @@ perform streamlines tracking.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/tensor2ADC.html b/doc/commands/tensor2ADC.html
index 1b48623..ecc91ad 100644
--- a/doc/commands/tensor2ADC.html
+++ b/doc/commands/tensor2ADC.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ calculate map of mean apparent diffusion coefficient (ADC) from diffusion tensor
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/tensor2FA.html b/doc/commands/tensor2FA.html
index 9351df6..e662a46 100644
--- a/doc/commands/tensor2FA.html
+++ b/doc/commands/tensor2FA.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ calculate map of fractional anisotropy from diffusion tensor image.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/tensor2vector.html b/doc/commands/tensor2vector.html
index b90bc0c..1f8262f 100644
--- a/doc/commands/tensor2vector.html
+++ b/doc/commands/tensor2vector.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -46,8 +46,8 @@ generate map of the major eigenvector.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/tensor_metric.html b/doc/commands/tensor_metric.html
index 2001a9f..7d327e3 100644
--- a/doc/commands/tensor_metric.html
+++ b/doc/commands/tensor_metric.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -80,8 +80,8 @@ generate maps of tensor-derived parameters.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/threshold.html b/doc/commands/threshold.html
index 4eed4a6..3644fee 100644
--- a/doc/commands/threshold.html
+++ b/doc/commands/threshold.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -67,8 +67,8 @@ By default, the threshold level is determined using a histogram analysis to cut
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/track_info.html b/doc/commands/track_info.html
index 14626d7..fd19a8d 100644
--- a/doc/commands/track_info.html
+++ b/doc/commands/track_info.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -50,8 +50,8 @@ print out information about track file
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/tracks2prob.html b/doc/commands/tracks2prob.html
index 79d8919..bf4f153 100644
--- a/doc/commands/tracks2prob.html
+++ b/doc/commands/tracks2prob.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -78,8 +78,8 @@ convert a tracks file into a map of the fraction of tracks to enter each voxel.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/tracks2vtk.html b/doc/commands/tracks2vtk.html
index 92250a2..0c04720 100644
--- a/doc/commands/tracks2vtk.html
+++ b/doc/commands/tracks2vtk.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -58,8 +58,8 @@ convert a track file to a vtk format, cave: coordinates are in XYZ coordinates n
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/commands/truncate_tracks.html b/doc/commands/truncate_tracks.html
index 471d5d2..15f4e9b 100644
--- a/doc/commands/truncate_tracks.html
+++ b/doc/commands/truncate_tracks.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -48,8 +48,8 @@ truncate a tracks file by selecting only the first N tracks.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/faq.html b/doc/faq.html
index 70c122a..aa4d27e 100644
--- a/doc/faq.html
+++ b/doc/faq.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -377,8 +377,8 @@ achieved as follows:
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/general/cmdline.html b/doc/general/cmdline.html
index 496786f..f76707a 100644
--- a/doc/general/cmdline.html
+++ b/doc/general/cmdline.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -354,8 +354,8 @@ and compute the corresponding tensor components. The resulting data set is then
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/general/formats.html b/doc/general/formats.html
index e0ccc54..cb22750 100644
--- a/doc/general/formats.html
+++ b/doc/general/formats.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -349,8 +349,8 @@ the <a href='overview.html#axes'>coordinate system convention</a> used by MRtrix
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/general/index.html b/doc/general/index.html
index db0d576..6a4640c 100644
--- a/doc/general/index.html
+++ b/doc/general/index.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-  <title>MRtrix 0.2.11 documentation</title>
+  <title>MRtrix 0.2.12 documentation</title>
   <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -74,8 +74,8 @@
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/general/mrview.html b/doc/general/mrview.html
index dfe6031..e0fb656 100644
--- a/doc/general/mrview.html
+++ b/doc/general/mrview.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -267,8 +267,8 @@ The Screen Capture tool is used to create simple snapshots of the contents of th
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/general/overview.html b/doc/general/overview.html
index 2ea46a2..e29dc11 100644
--- a/doc/general/overview.html
+++ b/doc/general/overview.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -184,8 +184,8 @@ Note also that pixel coordinates and study numbers start from zero. For example,
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/index.html b/doc/index.html
index 9b20342..f626f79 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -13,7 +13,7 @@
     <td><a href="index.html"><img src="left.png"></a></td>
     <td><a href="index.html"><img src="up.png"></a></td>
     <td><a href="index.html"><img src="home.png"></a></td>
-    <th>MRtrix 0.2.11 - table of contents</th>
+    <th>MRtrix 0.2.12 - table of contents</th>
     <td><a href="intro.html"><img src="right.png"></a></td>
   </tr>
 </table>
@@ -63,8 +63,8 @@
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/install/index.html b/doc/install/index.html
index 9bbfa60..d37a4e3 100644
--- a/doc/install/index.html
+++ b/doc/install/index.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -43,8 +43,8 @@
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/install/macosx.html b/doc/install/macosx.html
index f0383af..58fbb39 100644
--- a/doc/install/macosx.html
+++ b/doc/install/macosx.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -124,8 +124,8 @@ href=unix.html#config>Unix instructions</a>.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/install/unix.html b/doc/install/unix.html
index 5afed20..804182f 100644
--- a/doc/install/unix.html
+++ b/doc/install/unix.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -275,8 +275,8 @@ default values will be used instead.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/install/windows.html b/doc/install/windows.html
index c5e7198..54dc6f3 100644
--- a/doc/install/windows.html
+++ b/doc/install/windows.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -99,8 +99,8 @@ href="unix.html#compile">Unix installation instructions</a>.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/intro.html b/doc/intro.html
index 15d8fc9..9e1d1af 100644
--- a/doc/intro.html
+++ b/doc/intro.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="stylesheet.css" type="text/css" media=screen>
 </head>
 
@@ -155,8 +155,8 @@ For example, in Microsoft Windows, you may need to use the backslash <kbd>'\'</k
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 
diff --git a/doc/tractography/dwi.html b/doc/tractography/dwi.html
index 937d429..364e1aa 100644
--- a/doc/tractography/dwi.html
+++ b/doc/tractography/dwi.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -216,8 +216,8 @@ will scan the DICOM data set specified and store the DW scheme in the file <kbd>
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/tractography/index.html b/doc/tractography/index.html
index 6709537..caefa45 100644
--- a/doc/tractography/index.html
+++ b/doc/tractography/index.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -50,8 +50,8 @@
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/tractography/preprocess.html b/doc/tractography/preprocess.html
index b20e9fa..f8acd33 100644
--- a/doc/tractography/preprocess.html
+++ b/doc/tractography/preprocess.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -237,8 +237,8 @@ sidebar tool within <a href='../general/mrview.html'>MRview</a>.
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/tractography/roi.html b/doc/tractography/roi.html
index b55079b..d6c6b90 100644
--- a/doc/tractography/roi.html
+++ b/doc/tractography/roi.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -80,8 +80,8 @@ The 'ROI analysis' of MRView can be used to define such ROIs (see <a href='../ge
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/doc/tractography/tracking.html b/doc/tractography/tracking.html
index b0b2454..02238ac 100644
--- a/doc/tractography/tracking.html
+++ b/doc/tractography/tracking.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
-<title>MRtrix 0.2.11 documentation</title>
+<title>MRtrix 0.2.12 documentation</title>
 <link rel="stylesheet" href="../stylesheet.css" type="text/css" media=screen>
 </head>
 <body>
@@ -154,8 +154,8 @@ href='../commands/tracks2prob.html'>tracks2prob</a>.  </p>
 
 <p class=footer>
 Donald Tournier<br>
-MRtrix version 0.2.11<br>
-Last updated 2013-03-13
+MRtrix version 0.2.12<br>
+Last updated 2014-02-25
 </p>
 
 </body>
diff --git a/lib/file/dicom/image.cpp b/lib/file/dicom/image.cpp
index 553c2ae..eb9d4cc 100644
--- a/lib/file/dicom/image.cpp
+++ b/lib/file/dicom/image.cpp
@@ -459,7 +459,7 @@ namespace MR {
 
           if (G(n,3)) {
             float norm = Math::magnitude (frame.G);
-            G(n,3) *= norm;
+            G(n,3) *= norm*norm;
             if (norm) {
               float d[] = { frame.G[0]/norm, frame.G[1]/norm, frame.G[2]/norm };
               if (rotate_DW_scheme) {
diff --git a/lib/file/dicom/mapper.cpp b/lib/file/dicom/mapper.cpp
index 6ee57a8..466b20e 100644
--- a/lib/file/dicom/mapper.cpp
+++ b/lib/file/dicom/mapper.cpp
@@ -103,7 +103,7 @@ namespace MR {
             // if multi-frame, loop over frames in image:
             if (image.frames.size()) {
               std::sort (image.frames.begin(), image.frames.end());
-              for (std::vector< RefPtr<Frame> >::const_iterator frame_it = image.frames.begin(); frame_it != image.frames.end(); ++frame_it) 
+              for (std::vector< RefPtr<Frame> >::const_iterator frame_it = image.frames.begin(); frame_it != image.frames.end(); ++frame_it)
                 frames.push_back (&**frame_it);
             }
             // otherwise add image frame:
@@ -115,6 +115,9 @@ namespace MR {
 
         std::vector<guint> dim = Frame::count (frames);
 
+        if (dim[0]*dim[1]*dim[2] > frames.size())
+          throw Exception ("missing image frames for DICOM image \"" + H.name + "\"");
+
         if (dim[0] > 1) { // switch axes so slice dim is inner-most:
           std::vector<Frame*> list (frames);
           std::vector<Frame*>::iterator it = frames.begin();
@@ -196,25 +199,54 @@ namespace MR {
 
         Math::Matrix M(4,4);
 
-        M(0,0) = -image.orientation_x[0];
-        M(1,0) = -image.orientation_x[1];
-        M(2,0) = image.orientation_x[2];
-        M(3,0) = 0.0;
-
-        M(0,1) = -image.orientation_y[0];
-        M(1,1) = -image.orientation_y[1];
-        M(2,1) = image.orientation_y[2];
-        M(3,1) = 0.0;
-
-        M(0,2) = -image.orientation_z[0];
-        M(1,2) = -image.orientation_z[1];
-        M(2,2) = image.orientation_z[2];
-        M(3,2) = 0.0;
-
-        M(0,3) = -image.position_vector[0];
-        M(1,3) = -image.position_vector[1];
-        M(2,3) = image.position_vector[2];
-        M(3,3) = 1.0;
+        // If multi-frame, take the transform information from the sorted frames; the first entry in the
+        //   vector should be the first slice of the first volume
+        if (image.frames.size()) {
+
+          const Frame& frame (*image.frames[0]);
+          M(0,0) = -frame.orientation_x[0];
+          M(1,0) = -frame.orientation_x[1];
+          M(2,0) = +frame.orientation_x[2];
+          M(3,0) = 0.0;
+
+          M(0,1) = -frame.orientation_y[0];
+          M(1,1) = -frame.orientation_y[1];
+          M(2,1) = +frame.orientation_y[2];
+          M(3,1) = 0.0;
+
+          M(0,2) = -frame.orientation_z[0];
+          M(1,2) = -frame.orientation_z[1];
+          M(2,2) = +frame.orientation_z[2];
+          M(3,2) = 0.0;
+
+          M(0,3) = -frame.position_vector[0];
+          M(1,3) = -frame.position_vector[1];
+          M(2,3) = +frame.position_vector[2];
+          M(3,3) = 1.0;
+
+        } else {
+
+          M(0,0) = -image.orientation_x[0];
+          M(1,0) = -image.orientation_x[1];
+          M(2,0) = image.orientation_x[2];
+          M(3,0) = 0.0;
+
+          M(0,1) = -image.orientation_y[0];
+          M(1,1) = -image.orientation_y[1];
+          M(2,1) = image.orientation_y[2];
+          M(3,1) = 0.0;
+
+          M(0,2) = -image.orientation_z[0];
+          M(1,2) = -image.orientation_z[1];
+          M(2,2) = image.orientation_z[2];
+          M(3,2) = 0.0;
+
+          M(0,3) = -image.position_vector[0];
+          M(1,3) = -image.position_vector[1];
+          M(2,3) = image.position_vector[2];
+          M(3,3) = 1.0;
+
+        }
 
         H.set_transform (M);
 
diff --git a/lib/image/format/analyse.cpp b/lib/image/format/analyse.cpp
index d36bae8..0d2258a 100644
--- a/lib/image/format/analyse.cpp
+++ b/lib/image/format/analyse.cpp
@@ -134,9 +134,9 @@ namespace MR {
 
         // offset and scale:
         H.scale = get<float32> (&NH->scl_slope, is_BE);
-        if (finite(H.scale) && H.scale != 0.0) {
+        if (gsl_finite(H.scale) && H.scale != 0.0) {
           H.offset = get<float32> (&NH->scl_inter, is_BE);
-          H.offset = finite (H.offset) ? H.offset : 0.0;
+          H.offset = gsl_finite (H.offset) ? H.offset : 0.0;
         }
         else {
           H.scale = 1.0;
diff --git a/lib/image/format/nifti1.cpp b/lib/image/format/nifti1.cpp
index 55fbbd1..2f60a9c 100644
--- a/lib/image/format/nifti1.cpp
+++ b/lib/image/format/nifti1.cpp
@@ -128,9 +128,9 @@ namespace MR {
 
         // offset and scale:
         H.scale = get<float32> (&NH->scl_slope, is_BE);
-        if (finite(H.scale) && H.scale != 0.0) {
+        if (gsl_finite(H.scale) && H.scale != 0.0) {
           H.offset = get<float32> (&NH->scl_inter, is_BE);
-          H.offset = finite (H.offset) ? H.offset : 0.0;
+          H.offset = gsl_finite (H.offset) ? H.offset : 0.0;
         }
         else {
           H.scale = 1.0;
diff --git a/lib/image/name_parser.cpp b/lib/image/name_parser.cpp
index 95232de..06e6f59 100644
--- a/lib/image/name_parser.cpp
+++ b/lib/image/name_parser.cpp
@@ -218,13 +218,10 @@ namespace MR {
       String str;
       guint n = seq_index.size()-1;
       for (guint i = 0; i < array.size(); i++) {
-        if (array[i].is_string()) str += array[i].string();
-        else { 
-          gchar buf[array[i].size()+1];
-          g_sprintf (buf, "%*.*d", array[i].size(), array[i].size(), array[i].sequence()[indices[n]]);
-          str += buf;
-          n--; 
-        }
+        if (array[i].is_string()) 
+          str += array[i].string();
+        else 
+          str += printf ("%*.*d", array[i].size(), array[i].size(), array[i].sequence()[indices[n--]]);
       }
 
       return (Glib::build_filename (folder_name, str));
diff --git a/lib/image/object.cpp b/lib/image/object.cpp
index f587fc0..6a5eea7 100644
--- a/lib/image/object.cpp
+++ b/lib/image/object.cpp
@@ -253,7 +253,7 @@ namespace MR {
       start = 0;
       memset (stride, 0, MRTRIX_MAX_NDIMS*sizeof(gssize));
 
-      guint order[ndim()];
+      std::vector<guint> order (ndim());
       guint last = ndim()-1;
       for (int i = 0; i < ndim(); i++) {
         if (H.axes.axis[i] != Axis::undefined) order[H.axes.axis[i]] = i; 
diff --git a/lib/image/position.h b/lib/image/position.h
index 68666c3..cef01cc 100644
--- a/lib/image/position.h
+++ b/lib/image/position.h
@@ -145,7 +145,9 @@ namespace MR {
          * In this case, calling this function on real-valued data will produce undefined results */
         void        get (OutputType format, float& val, float& val_im);
 
-
+      // added for efficiency in undo/redo - totally unsafe
+        gsize getoffset() {return(offset);}
+      void setoffset(gsize off){offset = off;}
       protected:
         int       x[MRTRIX_MAX_NDIMS]; //!< the current image coordinates
         gsize     offset; //!< the offset in memory to the current voxel
diff --git a/lib/math/vector.h b/lib/math/vector.h
index 06e8272..c9285f0 100644
--- a/lib/math/vector.h
+++ b/lib/math/vector.h
@@ -139,9 +139,9 @@ namespace MR {
     inline gsl_vector *Vector::get_gsl_vector () const            { return (V); }
     inline void Vector::set_gsl_vector (gsl_vector* vec)          { if (V) gsl_vector_free (V); V = vec; }
     inline gsl_vector* Vector::disown_gsl_vector ()               { gsl_vector* vec = V; V = NULL; return(vec); }
-    inline double Vector::min () const                            { if (V) return (gsl_vector_min (V)); else return (NAN); }
+    inline double Vector::min () const                            { if (V) return (gsl_vector_min (V)); else return (GSL_NAN); }
     inline guint   Vector::min_index () const                     { if (V) return (gsl_vector_min_index (V)); else return (0); }
-    inline double Vector::max () const                            { if (V) return (gsl_vector_max (V)); else return (NAN); }
+    inline double Vector::max () const                            { if (V) return (gsl_vector_max (V)); else return (GSL_NAN); }
     inline guint   Vector::max_index () const                     { if (V) return (gsl_vector_max_index (V)); else return (0); }
     inline double Vector::magnitude () const                      { return (sqrt(norm2())); }
 
diff --git a/lib/mrtrix.cpp b/lib/mrtrix.cpp
index 5672051..4e871f0 100644
--- a/lib/mrtrix.cpp
+++ b/lib/mrtrix.cpp
@@ -82,7 +82,7 @@ namespace MR {
       stop = false;
       message = msg;
       if (target) multiplier = 100.0/((float) target);
-      else multiplier = NAN;
+      else multiplier = GSL_NAN;
       current_val = percent = 0;
       if (gsl_isnan (multiplier)) stop_watch.start();
       init_func ();
diff --git a/lib/mrtrix.h b/lib/mrtrix.h
index d5447d8..9711620 100644
--- a/lib/mrtrix.h
+++ b/lib/mrtrix.h
@@ -73,7 +73,7 @@
 
 #define MRTRIX_MAJOR_VERSION 0
 #define MRTRIX_MINOR_VERSION 2
-#define MRTRIX_MICRO_VERSION 11
+#define MRTRIX_MICRO_VERSION 12
 
 
 /** Prints the file and line number. Useful for debugging purposes. */
@@ -314,11 +314,11 @@ namespace MR {
     va_start (list, format);
     guint len = g_vsnprintf (NULL, 0, format, list) + 1;
     va_end (list);
-    gchar buf[len];
+    std::vector<gchar> buf (len);
     va_start (list, format);
-    g_vsnprintf (buf, len, format, list);
+    g_vsnprintf (&buf[0], len, format, list);
     va_end (list);
-    return (buf);
+    return (&buf[0]);
   }
 
 
diff --git a/lib/svn_revision.h b/lib/svn_revision.h
index a99ddf1..09612d7 100644
--- a/lib/svn_revision.h
+++ b/lib/svn_revision.h
@@ -1 +1 @@
-#define SVN_REVISION 373
+#define SVN_REVISION 410
diff --git a/matlab/write_mrtrix.m b/matlab/write_mrtrix.m
index 32a6af2..64e28a0 100644
--- a/matlab/write_mrtrix.m
+++ b/matlab/write_mrtrix.m
@@ -75,7 +75,9 @@ end
 fprintf (fid, [ '\ndatatype: ' datatype ]);
 
 if isstruct (image) && isfield (image, 'comments')
-  fprintf (fid, '\ncomments: %s', image.comments)
+  for i=1:numel(image.comments)
+    fprintf (fid, '\ncomments: %s', image.comments{i});
+  end
 end
 
 if isstruct (image) && isfield (image, 'transform')
diff --git a/src/dwi/SH.cpp b/src/dwi/SH.cpp
index adcf6d3..054912c 100644
--- a/src/dwi/SH.cpp
+++ b/src/dwi/SH.cpp
@@ -103,12 +103,12 @@ namespace MR {
       {
         SH.lmax (lmax);
         float cel = cos(elevation);
-        double AL [lmax+1];
+        std::vector<double> AL (lmax+1);
 
-        gsl_sf_legendre_sphPlm_array (lmax, 0, cel, AL);
+        gsl_sf_legendre_sphPlm_array (lmax, 0, cel, &AL[0]);
         for (int l = 0; l <= lmax; l+=2) SH(l,0) = AL[l];
         for (int m = 1; m <= lmax; m++) {
-          gsl_sf_legendre_sphPlm_array (lmax, m, cel, AL);
+          gsl_sf_legendre_sphPlm_array (lmax, m, cel, &AL[0]);
           float c = cos (m*azimuth);
           float s = sin (m*azimuth);
           for (int l = ((m&1) ? m+1 : m); l <= lmax; l+=2) {
@@ -368,7 +368,7 @@ namespace MR {
       {
         float sel = sin(elevation);
         bool atpole = sel < 1e-4;
-        float legendre[NforL(lmax)];
+        std::vector<float> legendre (NforL(lmax));
 
         amplitude = dSH_del = dSH_daz = d2SH_del2 = d2SH_deldaz = d2SH_daz2 = 0.0;
 
diff --git a/src/dwi/gradient.cpp b/src/dwi/gradient.cpp
index ccc0cbc..bd46a04 100644
--- a/src/dwi/gradient.cpp
+++ b/src/dwi/gradient.cpp
@@ -23,20 +23,24 @@
 #include "point.h"
 #include "math/matrix.h"
 #include "dwi/gradient.h"
+#include "dwi/shells.h"
 
 namespace MR {
   namespace DWI {
 
     void normalise_grad (Math::Matrix &grad)
     {
-      if (grad.columns() != 4) throw Exception ("invalid gradient matrix dimensions");
-
-      double norm;
+      if (grad.columns() != 4)
+          throw Exception ("invalid gradient matrix dimensions");
       for (guint i = 0; i < grad.rows(); i++) {
-        norm = grad(i,3) ? 1.0/sqrt(grad(i,0)*grad(i,0)+grad(i,1)*grad(i,1)+grad(i,2)*grad(i,2)) : 0.0;
-        grad(i,0) *= norm;
-        grad(i,1) *= norm;
-        grad(i,2) *= norm;
+        double norm = sqrt(grad(i,0)*grad(i,0)+grad(i,1)*grad(i,1)+grad(i,2)*grad(i,2));
+        if (norm) {
+          grad(i,0) /= norm;
+          grad(i,1) /= norm;
+          grad(i,2) /= norm;
+        } else {
+          grad(i,3) = 0; 
+        }
       }
     }
 
@@ -66,15 +70,23 @@ namespace MR {
 
     void guess_DW_directions (std::vector<int>& dwi, std::vector<int>& bzero, const Math::Matrix& grad)
     {
-      if (grad.columns() != 4) throw Exception ("invalid gradient encoding matrix: expecting 4 columns.");
-
-      dwi.clear();
-      bzero.clear();
-
-      for (int i = 0; i < (int) grad.rows(); i++) {
-        if (grad(i,3)) dwi.push_back (i);
-        else bzero.push_back (i);
-      }
+      if (grad.columns() != 4)
+        throw Exception ("invalid gradient encoding matrix: expecting 4 columns.");
+      Shells shells(grad);
+      int shell_count = shells.count();
+      if (shell_count < 1 || shell_count > sqrt(grad.rows()))
+        throw Exception ("Gradient encoding matrix does not represent a HARDI sequence!");
+      info ("found " + str (shell_count) + " shells");
+      Shell bzeroShell;
+      Shell dwiShell;
+      if (shell_count>1)
+        bzeroShell = shells.first();
+      dwiShell = shells.last();
+      if (shell_count>1)
+        info ("using " + str (bzeroShell.count()) + " volumes with b-value " + str (bzeroShell.avg_bval()) + " +/-" + str (bzeroShell.std_bval()) + " as b=0 volumes");
+      info ("using " + str (dwiShell.count()) + " volumes with b-value " + str (dwiShell.avg_bval()) + " +/-" + str (dwiShell.std_bval()) + " as diffusion-weighted volumes");
+      bzero = bzeroShell.idx();
+      dwi = dwiShell.idx();
     }
 
 
diff --git a/src/dwi/render_frame.cpp b/src/dwi/render_frame.cpp
index 3d1bcec..c2d70a7 100644
--- a/src/dwi/render_frame.cpp
+++ b/src/dwi/render_frame.cpp
@@ -25,7 +25,7 @@
 */
 
 #include <fstream>
-#include <glibmm/stringutils.h>
+#include <glibmm.h>
 #include <gdkmm/cursor.h>
 #include <gtkmm/main.h>
 #include <gtkmm/container.h>
@@ -88,7 +88,7 @@ namespace MR {
     {
       values_changed = true;
       values = new_values;
-      l0_term = NAN;
+      l0_term = GSL_NAN;
       if (values.size()) 
         l0_term = values[0];
       set_normalise (normalise);
diff --git a/src/dwi/render_frame.h b/src/dwi/render_frame.h
index 1626476..46158e1 100644
--- a/src/dwi/render_frame.h
+++ b/src/dwi/render_frame.h
@@ -58,7 +58,7 @@ namespace MR {
         void set_normalise (bool yesno = true) { 
           normalise = yesno; 
           if (!values.size()) return;
-          if (!finite (values[0])) return;
+          if (!gsl_finite (values[0])) return;
           float factor = ( normalise ? 1.0 : l0_term ) / values[0];
           for (guint n = 0; n < values.size(); ++n)
             values[n] *= factor;
diff --git a/src/dwi/renderer.cpp b/src/dwi/renderer.cpp
index aea96d4..3987961 100644
--- a/src/dwi/renderer.cpp
+++ b/src/dwi/renderer.cpp
@@ -24,7 +24,7 @@
 
 */
 
-#include <glibmm/stringutils.h>
+#include <glibmm.h>
 #include <gdkmm/cursor.h>
 #include <gtkmm/main.h>
 #include <gsl/gsl_sf_legendre.h>
diff --git a/src/dwi/sdeconv/MCMC.cpp b/src/dwi/sdeconv/MCMC.cpp
index 8a4368c..64804e4 100644
--- a/src/dwi/sdeconv/MCMC.cpp
+++ b/src/dwi/sdeconv/MCMC.cpp
@@ -252,7 +252,7 @@ namespace MR {
     FOD.zero();
 
     index_pos.clear();
-    min_fval = INFINITY;
+    min_fval = GSL_POSINF;
   }
 
 
@@ -435,7 +435,7 @@ namespace MR {
     tmp.multiply(Binv, p_sigs);
 
     guint leave_index = 0;
-    min_val = INFINITY;
+    min_val = GSL_POSINF;
     gdouble d;
     for (i = 0; i < tmp.size(); i++) {
       if (rcost[i] > 0.0) {
diff --git a/src/dwi/shells.h b/src/dwi/shells.h
new file mode 100644
index 0000000..515a966
--- /dev/null
+++ b/src/dwi/shells.h
@@ -0,0 +1,269 @@
+/*
+    Copyright 2013 Brain Research Institute, Melbourne, Australia
+
+    Written by B Jeurissen, 12/08/13.
+
+    This file is part of MRtrix.
+
+    MRtrix is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    MRtrix is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef __dwi_shells_h__
+#define __dwi_shells_h__
+
+#include "math/matrix.h"
+#include <list>
+#include <limits>
+
+namespace MR
+{
+  namespace DWI
+  {
+
+    class Shell
+    {
+
+    public:
+      Shell()
+      {
+        count_ = 0;
+        avg_bval_ = 0;
+        std_bval_ = 0;
+        min_bval_ = 0;
+        max_bval_ = 0;
+      }
+
+      Shell(std::vector<double> bvals, std::vector<int> clusters, int cluster)
+      {
+        count_ = 0;
+        avg_bval_ = 0;
+        std_bval_ = 0;
+        min_bval_ = std::numeric_limits<double>::max();
+        max_bval_ = std::numeric_limits<double>::min();
+        for (size_t j = 0; j < clusters.size(); j++) {
+          if (clusters[j] == cluster) {
+            idx_.push_back(j);
+            count_++;
+            avg_bval_ += bvals[j];
+            if (bvals[j]<min_bval_) {
+              min_bval_ = bvals[j];
+            }
+            if (bvals[j]>max_bval_) {
+              max_bval_ = bvals[j];
+            }
+          }
+        }
+        avg_bval_ = avg_bval_/count_;
+        for (size_t j = 0; j < clusters.size(); j++) {
+          if (clusters[j] == cluster) {
+            std_bval_ += pow(bvals[j]-avg_bval_,2);
+          }
+        }
+        std_bval_ = sqrt(std_bval_/count_);
+      }
+
+      std::vector<int> idx()
+      {
+        return idx_;
+      }
+
+      int count() const
+      {
+        return count_;
+      }
+
+      double avg_bval() const
+      {
+        return avg_bval_;
+      }
+
+      double std_bval() const
+      {
+        return std_bval_;
+      }
+
+      double min_bval() const
+      {
+        return min_bval_;
+      }
+
+      double max_bval() const
+      {
+        return max_bval_;
+      }
+
+      Shell& operator= (const Shell& rhs)
+      {
+        idx_ = rhs.idx_;
+        avg_bval_ = rhs.avg_bval_;
+        std_bval_ = rhs.std_bval_;
+        count_ = rhs.count_;
+        min_bval_ = rhs.min_bval_;
+        max_bval_ = rhs.max_bval_;
+        return *this;
+      }
+
+      friend std::ostream& operator <<(std::ostream& stream, const Shell& S)
+      {
+        stream << S.count() << std::endl;
+        stream << S.min_bval() << std::endl;
+        stream << S.max_bval() << std::endl;
+        stream << S.avg_bval() << std::endl;
+        stream << S.std_bval() << std::endl;
+        return stream;
+      }
+
+    private:
+      std::vector<int> idx_;
+      double avg_bval_;
+      double std_bval_;
+      int count_;
+      double min_bval_;
+      double max_bval_;
+    };
+
+
+    class Shells
+    {
+    public:
+      Shells(const Math::Matrix& grad, size_t minDirections = 6, double bvalue_threshold = GSL_NAN)
+      {
+        for (size_t i = 0; i < grad.rows(); i++) {
+          bvals.push_back(grad (i,3));
+        }
+        minBval = *std::min_element(bvals.begin(),bvals.end());
+        maxBval = *std::max_element(bvals.begin(),bvals.end());
+        if (!gsl_finite (bvalue_threshold)) {
+          bvalue_threshold = 100;
+          //bvalue_threshold = (maxBval-minBval)/(2.0*double(bvals.size()));
+          //if (!bvalue_threshold)
+          //  bvalue_threshold = 1;
+        }
+        clusterBvalues(minDirections, bvalue_threshold);
+        sortByBval();
+      }
+
+      int count()
+      {
+        return shells.size();
+      }
+
+      Shell& operator[] (const int i)
+      {
+        return shells[i];
+      }
+
+      Shell& first()
+      {
+        return shells.front();
+      }
+
+      Shell& last()
+      {
+        return shells.back();
+      }
+
+      void sortByCount()
+      {
+        std::sort (shells.begin(), shells.end(), countComp);
+      }
+
+      void sortByBval()
+      {
+        std::sort (shells.begin(), shells.end(), bvalComp);
+      }
+
+      friend std::ostream& operator <<(std::ostream& stream, const Shells& S)
+      {
+        for (std::vector<Shell>::const_iterator it = S.shells.begin(); it != S.shells.end(); ++it) {
+          stream << *it << std::endl;
+        }
+        return stream;
+      }
+
+    private:
+      std::vector<Shell> shells;
+      std::vector<double> bvals;
+      double minBval;
+      double maxBval;
+
+      static bool countComp (Shell a, Shell b)
+      {
+        return (a.count()<b.count());
+      }
+
+      static bool bvalComp (Shell a, Shell b)
+      {
+        return (a.avg_bval()<b.avg_bval());
+      }
+
+      void regionQuery (double p, std::vector<double> x, double eps, std::vector<int>& idx)
+      {
+        for (size_t i = 0; i < x.size(); i++) {
+          if (std::abs(p-x[i]) < eps) {
+            idx.push_back(i);
+          }
+        }
+      }
+
+      void clusterBvalues(size_t minDirections, double eps)
+      {
+        std::vector<bool> visited;
+        visited.resize(bvals.size(),false);
+
+        std::vector<int> cluster;
+        cluster.resize(bvals.size(),-1);
+        int clusterIdx = -1;
+
+        for (size_t ii = 0; ii < bvals.size(); ii++) {
+          if (!visited[ii]) {
+            visited[ii] = true;
+            std::vector<int> neighborIdx;
+            regionQuery(bvals[ii],bvals,eps,neighborIdx);
+            if (bvals[ii] > eps && neighborIdx.size() < minDirections) {
+              cluster[ii] = -1;
+            } else {
+              cluster[ii]=++clusterIdx;
+              for (size_t i = 0; i < neighborIdx.size(); i++) {
+                if (!visited[neighborIdx[i]]) {
+                  visited[neighborIdx[i]] = true;
+                  std::vector<int> neighborIdx2;
+                  regionQuery(bvals[neighborIdx[i]],bvals,eps,neighborIdx2);
+                  if (neighborIdx2.size() >= minDirections) {
+                    for (size_t j = 0; j < neighborIdx2.size(); j++) {
+                      neighborIdx.push_back(neighborIdx2[j]);
+                    }
+                  }
+                }
+                if (cluster[neighborIdx[i]] < 0) {
+                  cluster[neighborIdx[i]] = clusterIdx;
+                }
+              }
+            }
+          }
+        }
+        double minIdx = *std::min_element(cluster.begin(),cluster.end());
+        double maxIdx = *std::max_element(cluster.begin(),cluster.end());
+        for (size_t i = minIdx; i <= maxIdx; i++) {
+          Shell s(bvals,cluster,i);
+          shells.push_back(s);
+        }
+      }
+    };
+  }
+}
+
+#endif
+
diff --git a/src/dwi/tractography/tracker/base.cpp b/src/dwi/tractography/tracker/base.cpp
index 975d92e..3bccd7f 100644
--- a/src/dwi/tractography/tracker/base.cpp
+++ b/src/dwi/tractography/tracker/base.cpp
@@ -50,6 +50,7 @@ namespace MR {
         Base::Base (Image::Object& source_image, Properties& properties) :
           source (source_image),
           props (properties),
+          values (source.dim(3)), 
           total_seed_volume (0.0),
           step_size (0.1),
           threshold (0.1),
diff --git a/src/dwi/tractography/tracker/base.h b/src/dwi/tractography/tracker/base.h
index 2e96c26..502f9ef 100644
--- a/src/dwi/tractography/tracker/base.h
+++ b/src/dwi/tractography/tracker/base.h
@@ -133,10 +133,20 @@ namespace MR {
                 Point seed (Math::RNG& rng)
                 {
                   Point p;
+                  float val;
                   do {
                     p.set (lower[0]+rng.uniform()*(upper[0]-lower[0]), lower[1]+rng.uniform()*(upper[1]-lower[1]), lower[2]+rng.uniform()*(upper[2]-lower[2]));
-                    i.P (p);
-                  } while (i.value() < 0.5);
+                    if (no_interp) {
+                      i.set (0, int(p[0]+0.5));
+                      i.set (1, int(p[1]+0.5));
+                      i.set (2, int(p[2]+0.5));
+                      val = i.Image::Position::value();
+                    }
+                    else {
+                      i.P (p);
+                      val = i.value();
+                    }
+                  } while (val < 0.5);
                   return (i.P2R (p));
                 }
 
@@ -174,6 +184,7 @@ namespace MR {
             Image::Interp source;
             Properties& props;
             Math::RNG rng;
+            std::vector<float> values;
 
             ROISphere spheres;
             ROIMask   masks;
@@ -188,7 +199,7 @@ namespace MR {
             bool excluded, no_mask_interp, stop_when_included, entered_inclusion;
 
 
-            int get_source_data (const Point& p, float* values)
+            int get_source_data (const Point& p)
             {
               if (source.R (p)) return true;
               for (source.set(3,0); source[3] < source.dim(3); source.inc(3)) values[source[3]] = source.value();
diff --git a/src/dwi/tractography/tracker/dt_stream.h b/src/dwi/tractography/tracker/dt_stream.h
index 65751c5..467f2e2 100644
--- a/src/dwi/tractography/tracker/dt_stream.h
+++ b/src/dwi/tractography/tracker/dt_stream.h
@@ -66,8 +66,7 @@ namespace MR {
 
         inline float DTStream::get_EV (const Point& p)
         {
-          float values[source.dim(3)];
-          if (get_source_data (p, values)) return (-1.0);
+          if (get_source_data (p)) return (-1.0);
 
           for (int n = 0; n < source.dim(3); n++) 
             values[n] = values[n] > 0.0 ? -log (values[n]) : 1e-12;
diff --git a/src/dwi/tractography/tracker/sd_prob.cpp b/src/dwi/tractography/tracker/sd_prob.cpp
index 80efc7e..8e4c615 100644
--- a/src/dwi/tractography/tracker/sd_prob.cpp
+++ b/src/dwi/tractography/tracker/sd_prob.cpp
@@ -25,7 +25,6 @@
 */
 
 #include "dwi/tractography/tracker/sd_prob.h"
-#include "dwi/SH.h"
 
 namespace MR {
   namespace DWI {
@@ -58,26 +57,19 @@ namespace MR {
 
         bool SDProb::init_direction (const Point& seed_dir)
         {
-          float values [source.dim(3)];
-          if (get_source_data (pos, values)) return (true);
+          if (get_source_data (pos)) return (true);
 
           if (!seed_dir) {
             for (int n = 0; n < max_trials; n++) {
               dir.set (rng.normal(), rng.normal(), rng.normal());
               dir.normalise();
-              float val = precomputed ? 
-                SH::value_precomputed (values, dir) : 
-                SH::value (values, dir, lmax);
-
+              float val = SH_amplitude (dir); 
               if (!gsl_isnan (val)) if (val > init_threshold) return (false);
             } 
           }
           else {
             dir = seed_dir;
-            float val = precomputed ? 
-              SH::value_precomputed (values, dir) : 
-              SH::value (values, dir, lmax);
-
+            float val = SH_amplitude (dir);
             if (gsl_finite (val)) if (val > init_threshold) return (false);
           }
 
@@ -89,16 +81,12 @@ namespace MR {
 
         bool SDProb::next_point ()
         {
-          float values [source.dim(3)];
-          if (get_source_data (pos, values)) return (true);
+          if (get_source_data (pos)) return (true);
 
           float max_val = 0.0;
           for (int n = 0; n < 12; n++) {
             Point new_dir = new_rand_dir();
-            float val = precomputed ? 
-              SH::value_precomputed (values, new_dir) : 
-              SH::value (values, new_dir, lmax);
-
+            float val = SH_amplitude (new_dir);
             if (val > max_val) max_val = val;
           }
 
@@ -108,10 +96,7 @@ namespace MR {
 
           for (int n = 0; n < max_trials; n++) {
             Point new_dir = new_rand_dir();
-            float val = precomputed ? 
-              SH::value_precomputed (values, new_dir) : 
-              SH::value (values, new_dir, lmax);
-
+            float val = SH_amplitude (new_dir); 
             if (val > threshold) {
               if (val > max_val) info ("max_val exceeded!!! (val = " + str(val) + ", max_val = " + str (max_val) + ")");
               if (rng.uniform() < val/max_val) {
diff --git a/src/dwi/tractography/tracker/sd_prob.h b/src/dwi/tractography/tracker/sd_prob.h
index b3a30e4..019ed7f 100644
--- a/src/dwi/tractography/tracker/sd_prob.h
+++ b/src/dwi/tractography/tracker/sd_prob.h
@@ -24,6 +24,7 @@
 #define __dwi_tractography_tracker_sd_prob_h__
 
 #include "dwi/tractography/tracker/base.h"
+#include "dwi/SH.h"
 
 namespace MR {
   namespace DWI {
@@ -42,6 +43,12 @@ namespace MR {
             virtual bool  init_direction (const Point& seed_dir);
             virtual bool  next_point ();
 
+            float SH_amplitude (const Point& dir) const { 
+              return (precomputed ? 
+                SH::value_precomputed (&values[0], dir) : 
+                SH::value (&values[0], dir, lmax) );
+            }
+
             Point         new_rand_dir ();
         };
 
diff --git a/src/dwi/tractography/tracker/sd_stream.cpp b/src/dwi/tractography/tracker/sd_stream.cpp
index b2bd0c0..1d1033e 100644
--- a/src/dwi/tractography/tracker/sd_stream.cpp
+++ b/src/dwi/tractography/tracker/sd_stream.cpp
@@ -57,9 +57,14 @@ namespace MR {
 
         bool SDStream::init_direction (const Point& seed_dir)
         {
-          float values [source.dim(3)];
-          if (get_source_data (pos, values)) return (true);
-          return init_direction (seed_dir, values);
+          if (get_source_data (pos)) return (true);
+
+          if (!seed_dir) dir.set (rng.normal(), rng.normal(), rng.normal());
+          else dir = seed_dir;
+          dir.normalise();
+          float val = SH::get_peak (&values[0], lmax, dir, precomputed);
+          if (gsl_finite (val)) if (val > init_threshold) return (false);
+          return (true);
         }
 
 
@@ -67,9 +72,17 @@ namespace MR {
 
         bool SDStream::next_point ()
         {
-          float values [source.dim(3)];
-          if (get_source_data (pos, values)) return (true);
-          return next_point (values);
+          if (get_source_data (pos)) return (true);
+
+          Point prev_dir (dir);
+          dir.normalise ();
+          float val = SH::get_peak (&values[0], lmax, dir, precomputed);
+
+          if (!gsl_finite (val)) return (true);
+          if (val < threshold) return (true);
+          if (dir.dot (prev_dir) < min_dp) return (true);
+
+          return (false);
         }
 
 
diff --git a/src/dwi/tractography/tracker/sd_stream.h b/src/dwi/tractography/tracker/sd_stream.h
index 96d7f26..ce31282 100644
--- a/src/dwi/tractography/tracker/sd_stream.h
+++ b/src/dwi/tractography/tracker/sd_stream.h
@@ -42,30 +42,6 @@ namespace MR {
 
             virtual bool  init_direction (const Point& seed_dir);
             virtual bool  next_point ();
-
-            bool init_direction (const Point& seed_dir, const float* values)
-            {
-              if (!seed_dir) dir.set (rng.normal(), rng.normal(), rng.normal());
-              else dir = seed_dir;
-              dir.normalise();
-              float val = SH::get_peak (values, lmax, dir, precomputed);
-              if (gsl_finite (val)) if (val > init_threshold) return (false);
-              return (true);
-            }
-
-            bool next_point (const float* values)
-            {
-              Point prev_dir (dir);
-              dir.normalise ();
-              float val = SH::get_peak (values, lmax, dir, precomputed);
-
-              if (!gsl_finite (val)) return (true);
-              if (val < threshold) return (true);
-              if (dir.dot (prev_dir) < min_dp) return (true);
-
-              return (false);
-            }
-
             float         min_dp;
         };
 
diff --git a/src/histogram.cpp b/src/histogram.cpp
index 04693e5..6cab07b 100644
--- a/src/histogram.cpp
+++ b/src/histogram.cpp
@@ -44,14 +44,14 @@ namespace MR {
     do {
       float val = ima.re();
 
-      if (finite(val)) { 
+      if (gsl_finite(val)) { 
         guint pos = (guint) ( list.size() * ((val - min)/(max-min)) );
         if (pos >= list.size()) pos = list.size()-1;
         list[pos].frequency++;
       }
       if (ima.is_complex()) {
         val = ima.im();
-        if (finite(val)) {
+        if (gsl_finite(val)) {
           guint pos = (guint) ( list.size() * (val - min)/(max-min) );
           if (pos >= list.size()) pos = list.size()-1;
           list[pos].frequency++;
diff --git a/src/mrview/image.cpp b/src/mrview/image.cpp
index 5908ae9..53b322f 100644
--- a/src/mrview/image.cpp
+++ b/src/mrview/image.cpp
@@ -96,8 +96,8 @@ namespace MR {
 
     void Image::get_bounds (float xbounds[2], float ybounds[2], const Point& vx, const Point& vy, const Point& pos) const
     {   
-      xbounds[0] = ybounds[0] = INFINITY;
-      xbounds[1] = ybounds[1] = -INFINITY;
+      xbounds[0] = ybounds[0] = GSL_POSINF;
+      xbounds[1] = ybounds[1] = GSL_NEGINF;
 
       Math::Matrix M (3,3);
       Math::Vector x(3), y(3);
diff --git a/src/mrview/pane.cpp b/src/mrview/pane.cpp
index b7cf101..612af0f 100644
--- a/src/mrview/pane.cpp
+++ b/src/mrview/pane.cpp
@@ -93,7 +93,8 @@ namespace MR {
 
     Pane::Pane () : 
       FOV (GSL_NAN),
-      gl_area (*this) 
+      gl_area (*this),
+      prev_FOV (GSL_NAN)
     { 
       set_mode (0);
       set_shadow_type (Gtk::SHADOW_IN); 
diff --git a/src/mrview/sidebar/overlay.cpp b/src/mrview/sidebar/overlay.cpp
index e0c6da7..ad6935b 100644
--- a/src/mrview/sidebar/overlay.cpp
+++ b/src/mrview/sidebar/overlay.cpp
@@ -343,9 +343,9 @@ namespace MR {
       { 
         show_overlays.set_active (true);
 
-        min_value.set_range (-INFINITY, INFINITY);
+        min_value.set_range (GSL_NEGINF, GSL_POSINF);
         min_value.set_digits (4);
-        max_value.set_range (-INFINITY, INFINITY);
+        max_value.set_range (GSL_NEGINF, GSL_POSINF);
         max_value.set_digits (4);
 
         transparency.set_draw_value (false);
diff --git a/src/mrview/sidebar/roi_analysis.cpp b/src/mrview/sidebar/roi_analysis.cpp
index 95bf9ca..8ad0558 100644
--- a/src/mrview/sidebar/roi_analysis.cpp
+++ b/src/mrview/sidebar/roi_analysis.cpp
@@ -35,6 +35,13 @@ namespace MR {
         brush_size_frame ("brush size"), 
         transparency (0.0, 256, 1.0),
         brush_size (1.0, 20.0, 1.0),
+	brush3d("3D brush"),
+	isobrush("Isotropic brush"),
+        fill_button ("fill"),
+        undo_button ("undo"),
+        redo_button ("redo"),
+        duplicate_from_previous_button ("copy previous"),
+        duplicate_from_next_button ("copy next"),
         roi_list (*this)
       { 
         show_ROIs.set_active (true);
@@ -45,26 +52,45 @@ namespace MR {
 
         brush_size.set_draw_value (true);
         brush_size.set_value (1.0);
-
+	brush3d.set_active(false);
         roi_scrolled_window.add (roi_list);
         roi_scrolled_window.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
         roi_scrolled_window.set_shadow_type (Gtk::SHADOW_IN);
         roi_scrolled_window.set_border_width (3);
         roi_frame.add (roi_scrolled_window);
 
+        fill_button.set_tooltip_text ("fill region from crosshairs location\n\nShortcut: F");
+        undo_button.set_tooltip_text ("undo last edit\n\nShortcut: Ctrl+Z");
+        redo_button.set_tooltip_text ("re-apply last undo\n\nShortcut: Ctrl+Shift+Z");
+        duplicate_from_previous_button.set_tooltip_text ("copy contents of previous slice onto current slice\n\nShortcut: P");
+        duplicate_from_next_button.set_tooltip_text ("copy contents of next slice onto current slice\n\nShortcut: N");
+
         transparency_frame.add (transparency);
         brush_size_frame.add (brush_size);
-
         pack_start (show_ROIs, Gtk::PACK_SHRINK);
         pack_start (roi_frame);
         pack_start (transparency_frame, Gtk::PACK_SHRINK);
         pack_start (brush_size_frame, Gtk::PACK_SHRINK);
+        pack_start (brush3d, Gtk::PACK_SHRINK);
+        pack_start (isobrush, Gtk::PACK_SHRINK);
+        pack_start (undo_button, Gtk::PACK_SHRINK);
+        pack_start (redo_button, Gtk::PACK_SHRINK);
+        pack_start (fill_button, Gtk::PACK_SHRINK);
+        pack_start (duplicate_from_previous_button, Gtk::PACK_SHRINK);
+        pack_start (duplicate_from_next_button, Gtk::PACK_SHRINK);
+
+	isobrush.set_active();
         show_all();
 
         Window::Main->pane().activate (this);
 
         transparency.signal_value_changed().connect (sigc::mem_fun (*this, &ROIAnalysis::on_change));
         show_ROIs.signal_toggled().connect (sigc::mem_fun (*this, &ROIAnalysis::on_change));
+        undo_button.signal_clicked().connect (sigc::mem_fun (*this, &ROIAnalysis::on_undo));
+        redo_button.signal_clicked().connect (sigc::mem_fun (*this, &ROIAnalysis::on_redo));
+        fill_button.signal_clicked().connect (sigc::mem_fun (*this, &ROIAnalysis::on_fill));
+        duplicate_from_previous_button.signal_clicked().connect (sigc::mem_fun (*this, &ROIAnalysis::on_copy_previous));
+        duplicate_from_next_button.signal_clicked().connect (sigc::mem_fun (*this, &ROIAnalysis::on_copy_next));
       }
 
 
@@ -72,16 +98,23 @@ namespace MR {
 
       ROIAnalysis::~ROIAnalysis () {  }
 
-
-
       void ROIAnalysis::draw () { if (show_ROIs.get_active()) roi_list.draw ((int) transparency.get_value()); }
       void ROIAnalysis::on_change () { Window::Main->update (this); }
 
-      bool ROIAnalysis::on_button_press (GdkEventButton* event) { return (roi_list.on_button_press (event, brush_size.get_value())); }
-      bool ROIAnalysis::on_motion (GdkEventMotion* event) { return (roi_list.on_motion (event, brush_size.get_value())); }
+      bool ROIAnalysis::on_button_press (GdkEventButton* event) { return (roi_list.on_button_press (event, brush_size.get_value(), brush3d.get_active(), isobrush.get_active())); }
+      bool ROIAnalysis::on_motion (GdkEventMotion* event) { return (roi_list.on_motion (event, brush_size.get_value(), brush3d.get_active(), isobrush.get_active())); }
       bool ROIAnalysis::on_button_release (GdkEventButton* event) { return (roi_list.on_button_release (event)); }
 
+      bool ROIAnalysis::on_key_press (GdkEventKey* event)	
+      {
+	return(roi_list.on_key_press (event));
+      }
 
+      void ROIAnalysis::on_undo () { roi_list.undo(); }
+      void ROIAnalysis::on_redo () { roi_list.redo(); }
+      void ROIAnalysis::on_fill () { roi_list.floodfill(); }
+      void ROIAnalysis::on_copy_previous () { roi_list.copyslice (-1); }
+      void ROIAnalysis::on_copy_next () { roi_list.copyslice (1); }
 
     }
   }
diff --git a/src/mrview/sidebar/roi_analysis.h b/src/mrview/sidebar/roi_analysis.h
index 4f0db7f..e800aca 100644
--- a/src/mrview/sidebar/roi_analysis.h
+++ b/src/mrview/sidebar/roi_analysis.h
@@ -23,6 +23,7 @@
 #ifndef __mrview_sidebar_roi_analysis_h__
 #define __mrview_sidebar_roi_analysis_h__
 
+#include <gtkmm/button.h>
 #include <gtkmm/checkbutton.h>
 #include <gtkmm/scrolledwindow.h>
 #include <gtkmm/frame.h>
@@ -45,15 +46,23 @@ namespace MR {
           bool on_button_press (GdkEventButton* event);
           bool on_motion (GdkEventMotion* event);
           bool on_button_release (GdkEventButton* event);
-
+ 	  bool on_key_press (GdkEventKey* event);
         protected:
-          Gtk::CheckButton     show_ROIs;
+	  Gtk::CheckButton     show_ROIs;
           Gtk::Frame           roi_frame, transparency_frame, brush_size_frame;
           Gtk::HScale          transparency, brush_size;
+	  Gtk::CheckButton     brush3d;
+  	  Gtk::CheckButton     isobrush;
+          Gtk::Button          fill_button, undo_button, redo_button, duplicate_from_previous_button, duplicate_from_next_button;
           Gtk::ScrolledWindow  roi_scrolled_window;
           DP_ROIList           roi_list;
 
           void  on_change ();
+          void  on_undo ();
+          void  on_redo ();
+          void  on_fill ();
+          void  on_copy_previous ();
+          void  on_copy_next ();
       };
 
     }
diff --git a/src/mrview/sidebar/roi_analysis/roi_list.cpp b/src/mrview/sidebar/roi_analysis/roi_list.cpp
index 960c842..f1e6630 100644
--- a/src/mrview/sidebar/roi_analysis/roi_list.cpp
+++ b/src/mrview/sidebar/roi_analysis/roi_list.cpp
@@ -1,25 +1,26 @@
 /*
-    Copyright 2008 Brain Research Institute, Melbourne, Australia
+   Copyright 2008 Brain Research Institute, Melbourne, Australia
 
-    Written by J-Donald Tournier, 27/06/08.
+   Written by J-Donald Tournier, 27/06/08.
 
-    This file is part of MRtrix.
+   This file is part of MRtrix.
 
-    MRtrix is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
+   MRtrix is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-    MRtrix is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
+   MRtrix is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.
+   You should have received a copy of the GNU General Public License
+   along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.
 
-*/
+ */
 
+#include <glibmm.h>
 #include <gtkmm/treerowreference.h>
 #include <gtkmm/colorselection.h>
 #include <gtkmm/stock.h>
@@ -30,6 +31,8 @@
 #include "mrview/window.h"
 #include "dialog/file.h"
 
+#include <queue>
+
 namespace MR {
   namespace Viewer {
     namespace SideBar {
@@ -59,6 +62,9 @@ namespace MR {
 
         Gtk::CellRendererToggle* tick = dynamic_cast<Gtk::CellRendererToggle*> (get_column_cell_renderer (tick_column_index));
         tick->signal_toggled().connect (sigc::mem_fun (*this, &DP_ROIList::on_tick));
+
+        MaxUndoSize=10;
+
       }
 
 
@@ -288,52 +294,378 @@ namespace MR {
 
 
       void DP_ROIList::on_tick (const String& path) { Window::Main->update (&parent); }
-      
-      bool DP_ROIList::on_button_press (GdkEventButton* event, float brush) 
+
+
+      bool DP_ROIList::on_button_press (GdkEventButton* event, float brush, bool brush3d, bool isobrush) 
       {
         Gtk::TreeModel::iterator iter = get_selection()->get_selected();
         if (!iter) return (false);
-	guint state = (event->state & MODIFIERS) & (~GDK_BUTTON1_MASK);
+        guint state = (event->state & MODIFIERS) & (~GDK_BUTTON1_MASK);
         if (state != GDK_SHIFT_MASK && 
             state != ( GDK_SHIFT_MASK | CTRL_CMD_MASK ))
           return (false);
+
         row = *iter;
         bool show = row[columns.show];
         if (!show) return (false);
 
         set = state == GDK_SHIFT_MASK;
         editing = true;
+        process (event->x, event->y, brush, brush3d, isobrush);
+        return (true);
+      }
 
-        process (event->x, event->y, brush);
+      bool DP_ROIList::set_selected_row ()
+      {
+        Gtk::TreeModel::iterator iter = get_selection()->get_selected();
+        if (!iter) return (false);
+        row = *iter;
+        bool show = row[columns.show];
+        if (!show) return (false);
         return (true);
       }
 
 
+      bool DP_ROIList::on_key_press (GdkEventKey* event)	
+      {
+        // ignore releases
+        if (event->type == GDK_KEY_RELEASE) return (false);
+        switch (event->keyval) {
+          case GDK_F:
+          case GDK_f: 
+            return floodfill();
+	    break;
+          case GDK_N:
+          case GDK_n: 
+            return copyslice (1);
+	    break;
+          case GDK_P:
+          case GDK_p: 
+            return copyslice (-1);
+	    break;
+          case GDK_Z:
+            return (event->state & CTRL_CMD_MASK) ? redo() : false;
+	    break;
+          case GDK_z:
+            return (event->state & CTRL_CMD_MASK) ? undo() : false;
+	    break;
+          default:
+            break;
+        }
+        return false;
+      }
+
+
+
+
+
+
+      bool DP_ROIList::undo () 
+      {
+        if (!set_selected_row())
+          return false;
+
+        if (UndoQueue.size() > 0) {
+          EdVecType ThisUndo = UndoQueue.front();
+          UndoQueue.pop_front();
+          ApplyUndo(ThisUndo);
+          RedoQueue.push_front(ThisUndo);
+        }
+
+        return true;
+      }
+
+
+      bool DP_ROIList::redo () 
+      {
+        if (!set_selected_row())
+          return false;
+
+        if (RedoQueue.size() > 0) {
+          EdVecType ThisUndo = RedoQueue.front();
+          RedoQueue.pop_front();
+          ApplyUndo(ThisUndo);
+          UndoQueue.push_front(ThisUndo);
+        }
+
+        return true;
+      }
+
+
+      void DP_ROIList::ApplyUndo(EdVecType &EV)
+      {
+        editing=true;
+        RefPtr<ROI> roi = row[columns.roi];
+        MR::Image::Position ima (*roi->mask->image);
+        for (unsigned k=0; k < EV.size(); k++)
+        {
+          bool newval=EV[k].value;
+          ima.setoffset(EV[k].offset);
+          EV[k].value = ima.value();
+          ima.value(newval);
+        }
+        editing=false;
+        Window::Main->update (&parent);
+      }
+
+
+      void DP_ROIList::AddToUndo(EdVecType EV)
+      {
+        UndoQueue.push_front(EV);
+        // as soon as something is edited, we clear the redo list
+        RedoQueue.clear();
+        if (UndoQueue.size() > MaxUndoSize)
+          UndoQueue.pop_back();
+      }
+
+
+      bool DP_ROIList::copyslice (gint offset)
+      {
+        if (!set_selected_row())
+          return false;
+
+        editing=true;
+
+        RefPtr<ROI> roi = row[columns.roi];
+        // figure out our current slice
+        Point pos (roi->mask->interp->R2P (position (0, 0)));
+        MR::Image::Position ima (*roi->mask->image);
+        MR::Image::Position imb (*roi->mask->image);
+        int p[] = { round (pos[0]), round(pos[1]), round(pos[2]) };
+
+        Pane& pane (Window::Main->pane());
+        const Slice::Current S (pane);
+        unsigned projection(S.projection);
+
+        int sourceslice =  p[projection] + offset;
+        // don't do anything if the source slice is invalid
+        if ((sourceslice < 0) || (sourceslice >= ima.dim(projection))) {
+          std::cout << "Slice copy : out of range" << std::endl;
+	  editing=false;
+          return false;
+        }
+        unsigned int ax1 = 0, ax2 = 1;
+        switch (projection)
+        {
+          case 0:
+            ax1 = 1; ax2 = 2;
+            break;
+          case 1:
+            ax1 = 0; ax2 = 2;
+            break;
+          case 2:
+            ax1 = 0; ax2 = 1;
+            break;
+          default:
+            break;
+        }
+        // ima points to current slice
+        ima.set (projection, p[projection]);
+        ima.set (ax1, 0);
+        ima.set (ax2, 0);
+
+        // imb points to the source slice
+        imb.set (projection, sourceslice);
+        imb.set (ax1, 0);
+        imb.set (ax2, 0);
+        EdVecType UndoVec;
+
+        for (imb.set(ax1, 0), ima.set(ax1, 0); ima[ax1] < ima.dim(ax1); ima.inc(ax1), imb.inc(ax1)) {
+          for (imb.set(ax2, 0), ima.set(ax2, 0); ima[ax2] < ima.dim(ax2); ima.inc(ax2), imb.inc(ax2)) {
+            AddVox(ima, UndoVec);
+            ima.value(imb.value());
+          }
+        }
+        AddToUndo(UndoVec);
+        Window::Main->update (&parent);
+
+        editing=false;
+        return true;
+      }
+
+
 
 
+      bool DP_ROIList::floodfill ()
+      {
+        if (!set_selected_row())
+          return false;
 
+        editing=true;
 
+        Pane& pane (Window::Main->pane());
+        const Slice::Current S (pane);
+
+        RefPtr<ROI> roi = row[columns.roi];
+        Point pos (roi->mask->interp->R2P (S.focus));
+        MR::Image::Position ima (*roi->mask->image);
+        int p[] = { round (pos[0]), round(pos[1]), round(pos[2]) };
+
+        EdVecType UndoInfo;
+
+        // set the position we are starting from
+        ima.set(0, p[0]);
+        ima.set(1, p[1]);
+        ima.set(2, p[2]);
+        // should we adapt to be flood delete too??
+        float bgvalue = ima.value();
+        float fillvalue = !bgvalue;
+        {
+
+          // we are only flooding in 2D, so pick which axes this
+          // corresponds to.
+          unsigned ax1=0, ax2=1;
+          switch (S.projection)
+          {
+            case 0:
+              ax1=1; ax2=2;
+              break;
+            case 1:
+              ax1=0; ax2=2;
+              break;
+            case 2:
+              ax1=0; ax2=1;
+              break;
+            default:
+              break;
+          }
+          // set the other values
+          // standard flooding algorithm :
+          // put voxel on queue
+          // Pop top of queue: visit neighbours : label unlabelled
+          // neighbours and return place them on queue
+          std::queue<MR::Image::Position> fqueue;
+          AddVox(ima, UndoInfo);
+          ima.value(fillvalue);
+          fqueue.push(ima);
+
+          int offset1[8]={-1, -1, -1, 0, 0, 1, 1, 1};
+          int offset2[8]={-1, 0, 1, -1, 1, -1, 0, 1};
+
+          while (!fqueue.empty()) {
+            MR::Image::Position idx = fqueue.front();
+            fqueue.pop();
+            for (unsigned P=0;P<8;P++) {
+              MR::Image::Position nidx = idx;	
+              // set up the neighbour
+              int a1 = idx[ax1]+offset1[P];
+              int a2 = idx[ax2]+offset2[P];
+              if (!((a1 < 0) || (a2 < 0) || (a1 >= idx.dim(ax1)) || (a2 >= idx.dim(ax2)) ) ) {
+                nidx.set(ax1, a1);
+                nidx.set(ax2, a2);
+                if (nidx.value()==bgvalue) {
+                  AddVox(nidx, UndoInfo);
+                  nidx.value(fillvalue);
+                  fqueue.push(nidx);
+                }
+              }
+            }
+          }
+        }
+        AddToUndo(UndoInfo);
+        Window::Main->update (&parent);
+        editing = false;
 
-      void DP_ROIList::process (gdouble x, gdouble y, float brush)
+        return true;
+      }
+
+
+
+
+
+      void DP_ROIList::process (gdouble x, gdouble y, float brush, bool brush3d, bool isobrush)
       {
         RefPtr<ROI> roi = row[columns.roi];
         Point pos (roi->mask->interp->R2P (position (x, y)));
+
         MR::Image::Position ima (*roi->mask->image);
         int p[] = { round (pos[0]), round(pos[1]), round(pos[2]) };
         int e = ceil(brush/2.0);
+        int e0(e), e1(e), e2(e);
         float dist = (brush*brush)/4.0;
-        for (ima.set (2, p[2]-e); ima[2] <= p[2]+e; ima.inc(2)) {
-          if (ima[2] < 0 || ima[2] >= ima.dim(2)) continue;
-          for (ima.set (1, p[1]-e); ima[1] <= p[1]+e; ima.inc(1)) {
-            if (ima[1] < 0 || ima[1] >= ima.dim(1)) continue;
-            for (ima.set (0, p[0]-e); ima[0] <= p[0]+e; ima.inc(0)) {
-              if (ima[0] < 0 || ima[0] >= ima.dim(0)) continue;
-              if ((ima[0]-p[0])*(ima[0]-p[0]) + (ima[1]-p[1])*(ima[1]-p[1]) + (ima[2]-p[2])*(ima[2]-p[2]) < dist)
-                ima.value (set ? 1.0 : 0.0);
+        float Sc0(1), Sc1(1), Sc2(1);
+        if (isobrush)
+        {
+          // interpret size as mm
+          float v0 = ima.vox(0);
+          float v1 = ima.vox(1);
+          float v2 = ima.vox(2);
+
+          e0 = ceil(e/v0);
+          e1 = ceil(e/v1);
+          e2 = ceil(e/v2);
+          Sc0=(v0*v0);
+          Sc1=(v1*v1);
+          Sc2=(v2*v2);
+        }
+
+        Pane& pane (Window::Main->pane());
+        const Slice::Current S (pane);
+
+        if (brush3d) {
+          for (ima.set (2, p[2]-e2); ima[2] <= p[2]+e2; ima.inc(2)) {
+            if (ima[2] < 0 || ima[2] >= ima.dim(2)) continue;
+            for (ima.set (1, p[1]-e1); ima[1] <= p[1]+e1; ima.inc(1)) {
+              if (ima[1] < 0 || ima[1] >= ima.dim(1)) continue;
+              for (ima.set (0, p[0]-e0); ima[0] <= p[0]+e0; ima.inc(0)) {
+                if (ima[0] < 0 || ima[0] >= ima.dim(0)) continue;
+                if ((ima[0]-p[0])*(ima[0]-p[0])*Sc0 + (ima[1]-p[1])*(ima[1]-p[1])*Sc1 + (ima[2]-p[2])*(ima[2]-p[2])*Sc2 < dist) {
+                  AddVox(ima, processUndoBuff, set ? 1.0 : 0.0);
+                  ima.value (set ? 1.0 : 0.0);
+                }
+              }
             }
           }
         }
-
+        else {
+          const unsigned projection(S.projection);
+          switch (projection) {
+            case 0:
+              // sagittal
+              ima.set (0, p[0]);
+              for (ima.set (2, p[2]-e2); ima[2] <= p[2]+e2; ima.inc(2)) {
+                if (ima[2] < 0 || ima[2] >= ima.dim(2)) continue;
+                for (ima.set (1, p[1]-e1); ima[1] <= p[1]+e1; ima.inc(1)) {
+                  if (ima[1] < 0 || ima[1] >= ima.dim(1)) continue;
+                  if (Sc1*(ima[1]-p[1])*(ima[1]-p[1]) + Sc2*(ima[2]-p[2])*(ima[2]-p[2]) < dist) {
+                    AddVox(ima, processUndoBuff, set ? 1.0 : 0.0);
+                    ima.value (set ? 1.0 : 0.0);
+                  }
+                }
+              }
+              break;
+            case 1:
+              // coronal
+              ima.set (1, p[1]);
+              for (ima.set (2, p[2]-e2); ima[2] <= p[2]+e2; ima.inc(2)) {
+                if (ima[2] < 0 || ima[2] >= ima.dim(2)) continue;
+                for (ima.set (0, p[0]-e0); ima[0] <= p[0]+e0; ima.inc(0)) {
+                  if (ima[0] < 0 || ima[0] >= ima.dim(0)) continue;
+                  if (Sc0*(ima[0]-p[0])*(ima[0]-p[0]) + Sc2*(ima[2]-p[2])*(ima[2]-p[2]) < dist) {
+                    AddVox(ima, processUndoBuff, set ? 1.0 : 0.0);
+                    ima.value (set ? 1.0 : 0.0);
+                  }
+                }
+              }
+              break;
+            case 2:
+              // axial
+              ima.set (2, p[2]);
+              for (ima.set (1, p[1]-e1); ima[1] <= p[1]+e1; ima.inc(1)) {
+                if (ima[1] < 0 || ima[1] >= ima.dim(1)) continue;
+                for (ima.set (0, p[0]-e0); ima[0] <= p[0]+e0; ima.inc(0)) {
+                  if (ima[0] < 0 || ima[0] >= ima.dim(0)) continue;
+                  if (Sc0*(ima[0]-p[0])*(ima[0]-p[0]) + Sc1*(ima[1]-p[1])*(ima[1]-p[1]) < dist) {
+                    AddVox(ima, processUndoBuff, set ? 1.0 : 0.0);
+                    ima.value (set ? 1.0 : 0.0);
+                  }
+                }
+              }
+              break;
+            default:
+              assert (0);
+          }
+        }
         Window::Main->update (&parent);
       }
 
diff --git a/src/mrview/sidebar/roi_analysis/roi_list.h b/src/mrview/sidebar/roi_analysis/roi_list.h
index 2b388fd..2c5e4e6 100644
--- a/src/mrview/sidebar/roi_analysis/roi_list.h
+++ b/src/mrview/sidebar/roi_analysis/roi_list.h
@@ -29,6 +29,7 @@
 //#include <gtkmm/tooltip.h>
 
 #include "mrview/slice.h"
+#include <deque>
 
 namespace MR {
   namespace Viewer {
@@ -43,15 +44,74 @@ namespace MR {
           virtual ~DP_ROIList();
 
           void draw (int transparency);
-          bool on_button_press (GdkEventButton* event, float brush);
-          bool on_motion (GdkEventMotion* event, float brush) { if (editing) { process (event->x, event->y, brush); return (true); } return (false); };
-          bool on_button_release (GdkEventButton* event) { if (editing) { editing = false; return (true); } return (false); }
+	  bool on_button_press (GdkEventButton* event, float brush, bool brush3d, bool isobrush);
+  	  bool on_motion (GdkEventMotion* event, float brush, bool brush3d, bool isobrush) 
+	  { 
+	    if (editing) 
+	      { 
+	      process (event->x, event->y, brush, brush3d, isobrush);
+	      return (true); 
+	      } 
+	    return (false); 
+	  };
+
+          bool on_button_release (GdkEventButton* event) 
+	  { 
+	    if (editing) 
+	      { 
+	      editing = false;
+	      // push undo buffer onto queue, then clear it
+	      AddToUndo(processUndoBuff);
+	      processUndoBuff.clear();
+	      return (true); 
+	      } 
+	    return (false); 
+	  }
+
+	  bool on_key_press (GdkEventKey* event);
+
+          bool undo();
+          bool redo();
+          bool floodfill();
+	  bool copyslice(gint offset);
 
         protected:
           const ROIAnalysis& parent;
           bool  set, editing;
           Gtk::TreeModel::Row row;
 
+	  // not the most efficient way of doing this, but will do for starters.
+	  unsigned MaxUndoSize;
+          class EdVox {
+            public:
+              EdVox (bool value, gsize offset) : value (value), offset (offset) { }
+              bool value;
+              gsize offset;
+          };
+
+          typedef std::vector<EdVox> EdVecType;
+          typedef std::deque< EdVecType > EditQueueType;
+
+          // undo/redo queues
+          EditQueueType UndoQueue, RedoQueue;
+	  // a global undo buffer so we don't need to change the 
+          // mouse buttonpress interface
+	  EdVecType processUndoBuff;
+	  // applies the undo, and modifies EV to become a suitable redo list.
+	  void ApplyUndo(EdVecType &EV);
+	  void AddToUndo(EdVecType EV);
+
+          void AddVox(MR::Image::Position ima, EdVecType &EV) { EV.push_back (EdVox (ima.value(), ima.getoffset())); }
+
+          void AddVox(MR::Image::Position ima, EdVecType &EV, const float value)
+          {
+            float val = ima.value();
+              // only push if it has changed.
+              // specifically for drawing
+            if (val != value) 
+              EV.push_back (EdVox (val, ima.getoffset()));
+          }
+
           class ROI {
             public:
               ROI (RefPtr<MR::Image::Object> image, guint32 C) : mask (new Image (image)), render (false), colour (C) { mask->image->set_read_only (false);}
@@ -82,7 +142,9 @@ namespace MR {
           void on_clear ();
           void on_tick (const String& path);
 
-          void process (gdouble x, gdouble y, float brush);
+          bool set_selected_row ();
+
+	  void process (gdouble x, gdouble y, float brush, bool brush3d, bool isobrush);
           Point position (gdouble x, gdouble y);
 
           void load (RefPtr<MR::Image::Object> image);
diff --git a/src/mrview/sidebar/tractography.cpp b/src/mrview/sidebar/tractography.cpp
index b3a8474..a081f99 100644
--- a/src/mrview/sidebar/tractography.cpp
+++ b/src/mrview/sidebar/tractography.cpp
@@ -20,6 +20,7 @@
 
 */
 
+#include <glibmm.h>
 #include <gtkmm/treerowreference.h>
 
 #include "mrview/sidebar/tractography.h"
diff --git a/src/mrview/sidebar/tractography/roi_list.cpp b/src/mrview/sidebar/tractography/roi_list.cpp
index b7a7f69..65bebc3 100644
--- a/src/mrview/sidebar/tractography/roi_list.cpp
+++ b/src/mrview/sidebar/tractography/roi_list.cpp
@@ -20,6 +20,7 @@
 
 */
 
+#include <glibmm.h>
 #include "mrview/sidebar/tractography/roi_list.h"
 #include "mrview/sidebar/tractography/track_list.h"
 #include "mrview/sidebar/tractography.h"
diff --git a/src/mrview/sidebar/tractography/track_list.cpp b/src/mrview/sidebar/tractography/track_list.cpp
index 34286ba..5668f29 100644
--- a/src/mrview/sidebar/tractography/track_list.cpp
+++ b/src/mrview/sidebar/tractography/track_list.cpp
@@ -27,6 +27,7 @@
     
 */
 
+#include <glibmm.h>
 #include <gtkmm/treerowreference.h>
 #include <gtkmm/colorselection.h>
 #include <gtkmm/stock.h>
diff --git a/src/mrview/window.cpp b/src/mrview/window.cpp
index b07e0a4..c5b3989 100644
--- a/src/mrview/window.cpp
+++ b/src/mrview/window.cpp
@@ -20,6 +20,7 @@
 
 */
 
+#include <glibmm.h>
 #include <gtkmm/stock.h>
 #include <gtkmm/toggleaction.h>
 #include <gtkmm/aboutdialog.h>
diff --git a/sysconf/darwin.py b/sysconf/darwin.py
index a675462..d44795a 100644
--- a/sysconf/darwin.py
+++ b/sysconf/darwin.py
@@ -5,15 +5,16 @@ exe_suffix = ''
 lib_prefix = 'lib'
 lib_suffix = '.dylib'
 
-cpp = [ 'g++', '-c', '$flags$', '$gtk$', '$path$', '$src$', '-o', '$obj$' ]
-cpp_flags = [ '-Wall', '-mtune=native', '-fPIC', '-fno-strict-aliasing', '-DGL_GLEXT_PROTOTYPES', '-DUSE_TR1' ]
+cpp = [ 'clang++', '-c', '$flags$', '$gtk$', '$path$', '$src$', '-o', '$obj$' ]
+cpp_flags = [ '-Wall', '-mtune=native', '-fPIC', '-fno-strict-aliasing', '-DGL_GLEXT_PROTOTYPES' ]
 
-ld = [ 'g++', '$flags$', '$path$', '$obj$', '$mrtrix$', '$gsl$', '$gtk$', '$lz$', '-o', '$bin$' ]
-ld_flags = []
+ld_use_shell = True
+ld = [ 'clang++', '$flags$', '$path$', '$obj$', '$mrtrix$', '$gsl$', '$gtk$', '$lz$', '-o', '$bin$' ]
+ld_flags = [ '-Wl,-rpath, at loader_path/../lib ']
 ld_flags_lib_prefix = '-l'
 
-ld_lib = [ 'g++', '-shared', '$flags$', '$obj$', '-o', '$lib$' ]
-ld_lib_flags = []
+ld_lib = [ 'clang++', '-shared', '$flags$', '$obj$', '-o', '$lib$' ]
+ld_lib_flags = [ '-dynamiclib', '-install_name', '@rpath/LIBNAME' ]
 
 # look for MacPorts or FINK dependencies, and act accordingly if found:
 dep_dir = [ '/opt/local', '/sw64', '/sw' ]
@@ -32,6 +33,7 @@ cpp_flags_profile = [ '-pg' ] + cpp_flags_debug
 ld_flags_profile = ld_flags_debug + [ '-pg' ]
 ld_lib_flags_profile = ld_lib_flags_debug + [ '-pg' ]
 
+
 cpp_flags += [ '-O2' ]
 
 cpp_flags_release = [ '-DNDEBUG' ]
diff --git a/sysconf/linux.py b/sysconf/linux.py
index bdc3172..0b8ad7d 100644
--- a/sysconf/linux.py
+++ b/sysconf/linux.py
@@ -6,10 +6,10 @@ lib_prefix = 'lib'
 lib_suffix = '.so'
 
 cpp = [ 'g++', '-c', '$flags$', '$gtk$', '$path$', '$src$', '-o', '$obj$' ]
-cpp_flags = [ '-Wall', '-march=native', '-fPIC', '-fno-strict-aliasing', '-DGL_GLEXT_PROTOTYPES', '-DUSE_TR1' ]
+cpp_flags = [ '-Wall', '-pedantic', '-march=native', '-fPIC', '-fno-strict-aliasing', '-DGL_GLEXT_PROTOTYPES', '-DUSE_TR1' ]
 
 ld = [ 'g++', '$flags$', '$path$', '$obj$', '$mrtrix$', '$gsl$', '$gtk$', '$lz$', '-o', '$bin$' ]
-ld_flags = []
+ld_flags = [ '-Wl,-rpath,$ORIGIN/../lib' ]
 ld_flags_lib_prefix = '-l'
 
 ld_lib = [ 'g++', '-shared', '$flags$', '$obj$', '-o', '$lib$' ]

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/mrtrix.git



More information about the Debian-med-packaging mailing list