-------------------------------------------
tksurfer -- display surface & activation data
-------------------------------------------
The tcl-scriptable tksurfer program is started
either by the SURFACE button on the main csurf
panel, by the SURAFCE-STATS button on the
SessionTools -> View Functional Data panel, or
from the command line (see below).

It reads a surface file, a volume file, and
optionally, an area file, a curvature file, a
thickness file, a patch file, a label file, a
color lookup table file, and one or more
functional data overlay files, and then renders
an interactive view of the surface.  The surfer
tools panel is described from upper left to lower
right.

Context-dependent help

Every interface element (button, tick, entry,
bold label title) has a R-click help panel, which
are also all gathered in the csurf -> Help -> All
Help Contents panel.  For any button or tickbox
with multiple hotkey functions (some have up to
10), R-click help also brings up a button bar
with verbosely named buttons to control each
alternate function.

HOWTO display hi-res surface in MGH tksurfer

MGH tksurfer (now deprecated in MGH release)
requires a zero x,y,z voxelsize/spacing in the
mgz header, else it gives the error:

  "MatrixMultiply: m2 is null!."

Hi-res surfaces manually reconstructed from a
512^3 data set with csurf will have a voxel
size/spacing in the mgz header of 0.5 mm.  Use
calcimg to temporarily reset this to 0.0 to allow
MGH tksurfer to work:

  calcimg -spacing orig.mgz            # read
  calcimg -setspacing 0.0 orig.mgz   # change

Running tksurfer from a bash/tcsh shell script

N.B.: on MacOS 10.11+, the environment variable
DYLD_LIBRARY_PATH is *not* passed into a shell
script, which will prevent tksurfer from seeing
the csurf X11 tcl/tk libraries.  Therefore, to
use tksurfer in a MacOS 10.11+ shell script,
include the following line before the first call
to tksurfer:

sh/bash:
  export DYLD_LIBRARY_PATH=$CSURF_LIBRARY_PATH

csh/tcsh:
  setenv DYLD_LIBRARY_PATH $CSURF_LIBRARY_PATH

-------------------------------------------
Surface Image-Window Clicks
-------------------------------------------

(1) Select vertex, add to marked list (left-click)

   Left-click (select) on the surface selects the
   nearest vertex (defined relative to a point
   near the viewer, perpendicular to the clicked
   point) and adds it to the marked list (e.g.,
   to set up a cut).  On redraw, the vertices in
   the current marked list will appear white.  On
   a one-button Mac, a simple click is a
   left-click.

   Normally, the first vertex within 2mm of a
   perpendicular line from the screen surface to
   the cortical surface is selected.  This makes
   it hard to 'miss' a vertex.  However, in
   special cases (e.g., a 'deep canyon' in a
   highly folded cerebellar surface), this can
   end up selecting a vertex on one of the
   'canyon walls'.  To decrease the nearness
   criterion to 0.25 mm, click the "clo" button
   at the bottom middle (adjusts $selectdmax,
   N.B.: some clicks may now miss).

   This can be done in a tcl script by either:

     1) left_click <xpix> <ypix>
     2) leftclick_vertex <vertexnum>

(2) Unselect one vertex (middle-click)

   Remove a single vertex from the currently
   selected (ordered) list.

(3) Clear all selections/marks (right-click)

   Right-click (unselect/unmark) clears all the
   currently selected/marked vertices.  On a Mac
   laptop without a 3-button mouse (default
   X11.app setup), use cmd-click to get a
   right-click.  To remove a single point from
   the current list, use middle-click (opt-click
   on a one-button Mac).

   This can be done in a tcl script by:

     clear_vertex_marks_list

   If you accidentally clear a carefully-clicked
   list of selected vertices, Alt/Cmd-z will undo
   the clear and restore your selections.

(4) Rotate/Scale/Translate brain (shift click-drag)

   With shift depressed, you can rotate,
   translate, or scale the brain with a left,
   middle, or right click-drag.  N.B.: first
   focus on the brain surface window *before*
   depressing Shift (else the window can't detect
   that Shift has been pressed).  Unlike one-step
   scaling by crtl-left-click, scaling here does
   not center around the initial click point (too
   confusing).

   Can also rotate and translate from keyboard:
      Rotate -> L/R/up/down arrows
      Translate -> Shift-L/R/up/down arrows
      In-plane Rotate -> Ctrl-Shift-L/R arrows

(5) Zoom/Unzoom around point (ctrl-left/right click)

   For quick zoom, ctrl-left-click a surface
   point to zoom brain 200% around the clicked
   point.  To unzoom, use ctrl-right-click
   (ctrl-cmd-click on 1-button Mac)

(6) Dump voxel time course (ctrl-middle-click)

   If a stats overlay has been loaded and the
   current scandir also contains a raw time
   series data, a ctrl-middle-click will load the
   time series from the rawdata voxel that was
   painted onto the selected surface vertex.  The
   time series data will *not* be spatially
   smoothed (e.g., even if the surface stats have
   been smoothed).

   If the scan has been fourier-analyzed, the
   data for each stimulus cycle period (e.g.,
   eight 64 sec periods in an 8-cycle 512-sec
   scan) will be averaged and duplicated for each
   cycle in a thicker, lower-contrast line under
   the time course.  The color of the lines
   corresponds to the phase color of the stats
   at that vertex.  Note that a phase or surface
   average scandir typically *won't* contain raw
   data (a rawavg scandir *will*).

   An additional ctrl-middle-click loads a new
   single time courses.  Right-click to clear.

   N.B.: to do the same thing for all the
   vertices in a label, shift-left-click the
   "label:" line "T" button.

(7) Raise buttons interface window (double-left-click)

   A left double-click on the surface window will
   raise the tksurfer buttons interface window.

(8) Sphere img(s) val this vertex (shift-left-click)

   If surface data has been 'reverse painted'
   into a volume (used to drive multi-factor
   spherical morphing), a shift-left-click will
   sample the data voxel in which the current
   vertex resides (for sphere_morph operations).

(9) Popup name of annotation region (middle-double-click)

   If an annotation has been read onto the
   surface, a middle-double-click will popup the
   name (e.g., R_MT_ROI), rgb values (e.g.,
   31,99,108), idnum (e.g., 23), and neighbor
   count (e.g., 4).  To count neighbors,
   ctrl-mid-click "D" on the "label:" line
   (reports 0 before that).

(10) Click help (double-right-click)

   Pop-up help for all these different clicks

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

Tools Interface Window Clicks

  Left-click results in the default action for a
  button, radiobutton, or tickbox in the lower
  tksurfer tools interface window.

  Right-click on any interface element pops up a
  help window for it (there are almost 200
  pop-ups).  For some of the buttons with (too
  many) overloads using key-click combinations,
  R-click help also pops up a button menu with
  all the options.

  Less-used options are overloaded on buttons,
  radiobuttons, and tickboxes as a middle-click
  or a shift-middle-click (see right-click help
  for each button).

One Button Mouse or Trackpad

  On Mac, to get a middle or right click out of a
  1-button mouse or a trackpad, open X11 ->
  Preferences -> Input (Tab), tick "Emulate 3
  button mouse", and use:

    opt-click    =>  gives middle-click
    cmd-click  =>  gives right-click

  Thus, for direct click-drag manipulation of a
  surface on a one-button Mac:

    rotate      =>  shift, then click-drag
    translate  =>  shift-opt, then click-drag
       [N.B.: req's order: 1=opt,2=click,3=shift]
    scale      =>  shift-cmd, then click-drag

  or to dump timecourses from a clicked vertex:

    ctrl-opt-click

Window Resizing

  This is best and most repeatably done by typing
  in a new window size in pixels in the "winxy:"
  entry at the lower left.  Interactive resizing
  on Mac OS X causes too many redraws (difficult
  to constrain to square window with quartz-wm).

-------------------------------------------
Keyboard shortcuts -- for both image,tk windows
-------------------------------------------

Single key

  # interface size
  F2 -- basic interface   (Mac=fn-F2)
  F3 -- large interface   (Mac=fn-F3)

  # rotate,scale brain (interface win: add Mod)
  R/L arrow      -- rotate around vert axis
  up/down arrow  -- rotate around horiz axis
  Control-Shif-R/L arrow  -- rotate in plane
  Shift-R/L arrow      -- translate right/left
  Shift-up/down arrow  -- translate up/down
  plus (+)      -- scale up 1%
  equals (=)    -- same as plus
  minus (-)     -- scale down 1%

Modifier + key (LinuxMod="Alt", MacMod="Cmd")

  # SEND/GOTO
  Mod-f  -- SEND (forward) point
  Mod-g  -- GOTO point

  # REDRAW/RESTORE
  Mod-r  -- REDRAW (usu. automatic)
  Mod-R  -- RESTORE to original view

  # stat overlay
  Mod-l  -- (el) toggle overlay stats visibility

  # surface cursor size
  Mod-k  -- toggle big cursor

  # fmid (main threshold)
  Mod-i  -- lower fmid by 0.1 (sqrtF)
  Mod-I  -- raise fmid by 0.1 (sqrtF)
  Mod-u  -- lower fmid by 10.0 (raw fourieramp)
  Mod-U  -- raise fmid by 10.0 (raw fourieramp)

  # fslope (contrast)
  Mod-s  -- lower fslope by 0.5
  Mod-S  -- raise fslope by 0.5

  # angle_offset (rotate color wheel)
  Mod-o  -- (oh) lower angle_offset by 0.01
  Mod-O  -- (oh) raise angle_offset by 0.01

  # angle_cycles (compress hemi color wheel)
  Mod-y  -- lower angle_cycles by 0.1
  Mod-Y  -- raise angle_cycles by 0.1

  # misc
  Mod-L  -- thicken isocontour lines 1 pix
  Mod-F  -- kill ongoing FILL/ROI operation
-------------------------------------------

The tksurfer interface parts are described below
in two columns, starting from the top left.

-------------------------------------------
LEFT COLUMN OF TKSURFER INTERFACE
-------------------------------------------

Top left "surf:", "tcl:" entries, GO/EDIT buttons

The currently loaded data can be displayed on a
different surface for the same hemisphere by
selecting a surface from the dropdown or entering
the surface suffix (e.g., smoothwm, inflated) in
the "surf:" entry and hitting <Return>.  This
retains the current pose (magnification,
orientation).  The "inflated" surface will be
re-loaded if a bad surface is requested.  Don't
leave an invalid surface typed in *without* a
<Return>.

If tksurfer was started with a tcl script, the
script will be shown in the "tcl:" entry field.
The GO button executes the tcl script that is
specified there (re-running a standard script
will harmlessly complain if it tries to open
another OpenGL window).  A "#" in that "tcl:"
field is used as an abbreviation for the standard
script directory:

  $CSURF_DIR/lib/tcl

The "tcl:" dropdown shows standard library
scripts (see below) as well as any
session-specific scripts saved in
$session/image/scripts.

The EDIT button opens the current script (or
creates a new empty script if the named script
doesn't yet exist) in a text editor.  This is an
easy way to get access to the tcl interpreter
from the interface: EDIT a new script file (a
relative name like "test.tcl" will go to the
scripts directory for the current session), put a
command in it, save it, click GO to run it,
re-edit and repeat.  Return values and errors
appear in a popup window.  To work intensively
with the tcl interpreter, it is more convenient
to start up tksurfer from the command line (see
below).

See "Logging Interface Actions as Tcl Commands"
below for more information on writing your own
tcl scripts.

See tksurfer right-click pop-up help for the
"tcl:" entry for detailed explanations of what
the standard movie-making scripts do (they will
be visible in the "tcl:" dropdown):

  offsetmovie.tcl
  phasemovie.tcl
  movie360.tcl
  inflatemovie.tcl
  flattenmovie.tcl

For example, phasemovie.tcl, is useful for
visualizing the details of phase contour lines in
complex-valued data.

Surface Data File Entries/Dropdowns Overview

  curv  -- curv or sulc file (every vertex)
  area  -- original area file (every vertex)
  patch -- subset of surface, maybe flattened
  label -- labeled subset of vertices w/data
  lut   -- color look-up-table (LUT): val->r,g,b
  fs    -- fieldsign and fsmask files
  val   -- value overlay file (maybe vtx subset)
  rgb   -- tiff or SGI rgb output bitmap

The formats of these files are as follows (bytes
for each file element in brackets):

 old curv (binary, 2-byte int every vertex):
   vnum[3],fnum[3],{curv*100[2]}, ...

 new curv (binary, 4-byte float every vertex):
   -1[3],vnum[4],fnum[4],npervtx[4],{curv[4]},...

 old area (binary, 4-byte float every vertex):
   vnum[3],fnum[3],{area[4]}, ...

 new area (binary, every vertex):
   [same as new curv]

 old 2-byte short patch (binary, vertex subset):
   vnum[4],{vtx[4],x*100[2],y*100[2],z*100[2]}, ..

 new 4-byte float patch (binary, vertex subset):
   -1[4],vnum[4],{vtx[4],x[4],y[4],z[4]}, ...

 label file (ASCII, vertex subset, real or cmplx):
    #!ascii label , from subject marty <CR>
    <labeled-vtx-count> <CR>
    <vnum> <x> <y> <z> <realval> [imagval] <CR>
    <vnum> <x> <y> <z> <realval> [imagval] <CR>
    ...

 lut file (ASCII, variable number of line)
    #val   red   green   blue  (comment line)
    0.0    255     0      0
    1.0     0     255     0 
    ...

 fieldsign (binary, every vertex):
   fs (fieldsign): vnum[4],{float[4]}, ...
   fm (fssigmask): vnum[4],{float[4]}, ...

 val (binary, maybe vertex subset):
   latency[2],vnum[3],{vtx[3],val[4]}, ...

The binary formats are autodetected by tksurfer,
which will also accept a new curv format file as
a val file (N.B.: unlike val files, curv files
must contain every vertex).  See Help -> Csurf
for more details and a description of surface
formats and the end of this file for machine
independent C functions that read/write
Linux/IRIX/Darwin binary compatible FreeSurfer
files that contain 4-byte float, 4-byte ints,
3-byte ints, and 2-byte ints in MSB order (most
significant byte first).

The command line program, calcvert, can be used
to convert between formats, and to dump binary
vertex-wise formats to ASCII.  To dump surface
geometry data to ASCII, use paint -dump ...
(type "paint" for details).

In all the entry fields just described, a tilde
(~) can be used as an abbreviation of the path of
the current subject directory, such as:

  /usr0/subjects/marty

and an asterisk (*) can be used as an
abbreviation of the current functional session
(sub)directory, such as:

  /usr0/sessions/040115MS/image

Surface Data Example: "curv:"

The default curvature file displayed is
"$hemi.curv" -- the local curvature -- which is
measured with respect to the immediately
neighboring vertices around each vertex.  To read
in the "sulc" file instead of the "curv" file,
select it from the dropdown.  The sulc data is
the summed perpendicular movement of each vertex
during inflation.  It is used to drive morphing,
and is closer to sulcus-vs-gyrus than curvature,
which is a strictly local measure of the one
vertex in relation to its immediate neighbors
(concave-vs-convex).  An arbitrary curvature file
can be read by entering it and clicking "R" (full
path OK, too).

Surface Data Example: "area:"

The default area file read in is "$hemi.area" --
the original vertex-wise area calculated on the
$hemi.white surface.  The dropdown may also
include $hemi.area.mid and $hemi.area.pial.  The
original area is used and reported in the log and
a popup after a FILL operation.  By contrast,
writing a new area file with "W" calculates the
vertex-wise area of the *current* surface, and
writes it out to the file in the entry (there is
a warning if you try to overwrite the original
'original area').  The area at a vertex is
defined as the sum of 1/3 the area of each
adjacent triangular face.  The "D" button
displays log areal distortion of current surface
compared to original area ($hemi.area from
$hemi.white).

Surface Data Example: "patch:"

If flat patch(es) have been generated for this
surface, their names will appear in the patch
entry dropdown.  To view the patch and any data
currently displayed, select one from the "patch:"
dropdown.  The pose of this surface will respect
settings in Preferences -> Expert Preferences ->
Views tab (if the patch infix is "occip" or
"full").  To reload a full surface, select one
from the "surf:" dropdown.

Surface Data Example: make "label:" (label+data)

The buttons on the label line are overloaded.
The default action/function is a left-click.  A
different function is called with a middle-click,
or shift-middle-click, etc.  The R-click popup
helps for the label buttons have a summary of the
default and alternate actions at the top.

A label can be generated by:

  (1) cutting and filling a region
  (2) filling to statistical threshold
  (3) selecting vertices nearest curr selection
  (4) combinations of above

(1) To generate a label by cut and fill, first
outline a region with LEFT-Clicks (N.B.: cuts
done in order of clicks!) and use AREA to cut,
and then FILL to select it so that only the
labeled part is visible.  FILL respects cuts, but
also any any other currently displayed (D'd)
label (e.g., to make adjoining labels).

(2) To select a connected region based on the
current stats overlay up to the currently set
"fthresh", and use ROI.  Disconnected regions can
be combined into one label file by clicking
multiple points before clicking ROI.  This
respects cuts and other currently D'd labels.

(3) Finally, a label can be generated that
contains vertices nearest (in Euclidean distance
on current surface) to the currently selected
vertex.  The total number of vertices is
specified by either number ("N" button, entry
specifies vertex count), by area ("A", entry
specifies sq mm), or by radius ("R" button, entry
specifies mm).  See right-click help for those
buttons for more details.

Select a name for the label file and write it
with "W".  This saves the label location as well
as any currently displayed real or complex-valued
overlay data (it ignores D'd labels).  To undo
all cuts, use "INIT" (lower right).

Surface Data Example: display "label:" (label location)

To display just the location of the label, use
"D", which shows label as a transparent overlay
(transparency adjustable with "tran").  This uses
only the vertex numbers in the label file (vertex
x,y,z and value are ignored).  To toggle labels
display, use checkbutton to left of "label:".  To
clear all displayed labels, use "CLR".  The
tickbox is overloaded (e.g., shift-middle-click
to toggle viewing only label borders vs. filled
in labels).

Surface Data Example: display "label:" (MGH annotation)

The "D" button can also read an MGH annotation
file (e.g., rh.HCP-MMP1.annot) that contains
multiple labels and corresponding colors and ID
numbers (but no data).

Surface Data Example: find overlap of "label:" (2+)

To generate a new label from the overlap of two
(or more) labels, display and re-cut a first
label with "C", then display and re-cut a second
label (or a third, etc) with "C".  The end
result, which can be saved with "W", is just the
overlap.

Surface Data Example: use "label:" (label+data)

The "R" button reads *the data* in a label file
(real or complex -- last or two last entries each
line) into the val (and if complex, val2) vertex
field(s).  The adjoining "RC" button is similar,
but reads the label data and also re-cuts the
label (e.g., for re-edit or re-save with
different overlay data).  To completely clear the
*data* read in from one or more labels, use "CLR"
on the "val:" line.

The "T" button uses the current label to extract
the raw timecourse data (via register.dat,
assuming a rawdata file has been specified with
-rawdata) into an ASCII file in the current
scandir (these arguments are automatically given
to tksurfer if it is run from SessionTools ->
View Functional Data).

The "S" button performs the same operation as the
"T" button but also includes samples of 3D volume
stats file (possibly complex-valued) in the ASCII
output file (immediately before the timecourse).
See the right-click popup help for the S and T
buttons for more detals.

Left-click "label:" to get a "NormSamp (PAINT),
Cluster Filter Controls" pop-up for interactively
controlling the details of the norm search
(search along the normal at each surface vertex)
parameters for the "S" and "T" buttons.  These
parameters will be preset according to last saved
values in csurf SessionTools -> Setup Align/Funct
Scan panel for this scandir.  The 15 controls on
this panel are:

Ticks/Flags:

  normdsampsearchflag -- toggle search (else 1pnt)
  normdsampuniqvoxflag -- sample each *funct* vox once
  normdsampallflag -- dump full timecourse for each depth

  normdfracflag -- ON=thicknessfraction, OFF=mm
  bokflag -- modulate thickness depth frac by curvature

Numerical parameters:

  normdsamp -- 1pnt samp depth, or min depth (mm)
  normdmax -- max samp depth (mm) (1pnt:same as prev)

  normdfracsamp -- 1 samp depth, or min depth (fraction)
  normdfracmax -- max depth (fraction) (1pnt:same as prev)

  normddstep -- mm or depth fraction
  normdop -- operation on samples (0=max,1=avg,2=min)

  preclustfthr -- hard F thresh before surfclust
  clustarea -- min surfclust area to survive filter

  sulfrac -- strength of sulc Bok correction (-0.12)
  gyrfrac -- strength of gyrus Bok correction (0.16)

Here is how the Bok-like layer depth correction
is implemented (outline C-code) for the sampling
along the normal at one vertex:

/******************************************************/
for (dist=min; dist<=max; dist+=normddstep) {
  dist2 = dist;
  if (bokflag) { /*cosfunct: inp:0-1 out:0->1->0*/
    if (curv>0.0)
      dist2 += curv/curvmax * sulfrac*thickness *
              -cos(M_PI*dist/thickness + 0.5*M_PI);
    else
      dist2 += curv/curvmin * gyrfrac*thickness *
              -cos(M_PI*dist/thickness + 0.5*M_PI);
  }
  /* sample at dist2 up on normal to this vertex */
}
/******************************************************/

The "X" button uses the current label to define a
region over which to calculate the Pearson
correlation (or circular correlation) between two
vertexwise data sets for a set of searchlights
that each include nearest neighbors extending to
a radius defined by simple vertex count, surface
area (mm^2), or radius (mm).  See the "X"
right-click help for details on how to load the
two data sets.

Surface Data Example: "lut:" (color lookup table)

If activation overlay data is currently
displayed, you can dump out the current color
look up table as an ASCII file to the filename in
the "lut:" entry with the nearby "W" button.
"ED" can be used to edit the file, and "R" can be
used to re-read the edited file.  Each line in
the color look up table (LUT) maps data in the
val field of each vertex to r,g,b colors (0-255
for each color), interpolating intermediate value
(interpolation can be turned off with "intplut").
For real data, the first column is val; for
complex data, the first column is phase angle
where 0 -> 2Pi is 0.0 to 1.0 (the color scale
wraps around in this case).

Surface Data Example: "fs:" (fieldsign)

The fieldsign and fieldsign mask files are
automatically generated using the SessionTools ->
Calculate Visual Fieldsign panel.  Gradients (on
curvature, real, or complex data) and fieldsign
can be manually read, written, and calculated
with the "R", "W", "F" (fieldsign) and "GR"
(gradient) buttons.  See R-click popup help for
each button for details.

Surface Data Example: "val:" (vertex value)

Value files (*.w files) are generated when PAINT
is clicked on several different panels.  They may
be real or complex.  Use the "val:" CLR to clear
the value field (both real and imaginary) to 0.
Any value file with a *.w suffix will be
selectable from the dropdown.  Use "R" to read
the file.  By default, the file is assumed to be
real-valued.  If the file contains an infix
indicating it is complex-valued (_r,_i, or
_x,_y), both members of the pair will be read
(whichever is selected).

Finally, optionally smooth real or complex data
with SMOOTH.  This sequence of actions is
typically performed by the standard scripts that
read complex data such as polar-views.tcl.  To
save the smoothed data, use "W" (usu. better to
resmooth).

The "S/V" button on the "val:" line swaps the
values at the stat and val fields at each vertex.
To read in a data file and mask it with a
statistics file, first enter the stat file name
and click "R", then click "S/V", then enter the
val file name (real or complex) and click "R"
again.

Surface Data Example: "rgb:"

This field saves a bitmap of the current view, by
default a tiff.  To get a classic SGI rgb file,
unlick the check just to the left of WRITE, or
unclick Preferences -> Save .tiff in csurf and
restart tksurfer.

On Linux/IRIX, when WRITE'ing an rgb file, make
sure that the surfer display window must be
completely onscreen.  Partly offscreen is OK
w/Darwin.

The window will be raised to the front
immediately before saving to avoid capturing
overlapping windows.  The mouse cursor is
ignored.  To preserve cyan vertex selections, use
"W", which doesn't raise or redraw first (in that
case, be sure the window is in front of other
windows before saving).

Logs

Clicking on a vertex displays some of the data
associated with it, either at the tcl prompt if
tksurfer was started from the command line, or in
the csurf window, if Preferences -> View Logs is
clicked.  See also Preferences -> Bigger Logs.

Note that after surfer tools operations, these
values may differ from what was read in from from
curv/sulc and *.w files.  For example, smoothing
changes the real (and imaginary) values, while
curvature is typically slightly changed when it
is rescaled to half convex and half concave.

For complex-valued data, the log reports (among
other things):

  val   => real stat value (top pos data stack)
  val2  => imaginary stat value (2nd pos stack)
  amp   => amplitude   -- hypot(val,val2)
  angle => phase angle -- atan2(val2,val)=>deg

Logging Interface Actions as Tcl Commands

Clicking the "tcl:" label near the top left of
the tksurfer interface turns on the logging of
all interface actions as tcl commands to the
file, tksurfer.log, in:

  $FUNCTIONALS_DIR/<session>/image/scripts

These can be used to record actions to make a
command line tcl script to automate tksurfer
actions.  The "tcl:" label turns bold during
logging.  To finish logging, click the label
again.  Or you can startup tksurfer from csurf
with the log on by selecting

  Preferences -> Log tksurfer actions as tcl cmds

Note that in both cases, any previous log of tcl
commands in tksurfer.log in this session's script
dir is overwritten so each log starts fresh.

ROTATE/TRANSLATE/SCALE Sliders

  ROTATE
    horizontal slider -- rotate around vertical
    vertical slider -- rotate around horizontal
    yrot field -- rotate around y axis (deg)
    xrot field -- rotate around x axis (deg)
    zrot field -- rotate around z axis (deg)

  TRANSLATE
    horizontal slider -- translate left-right 
    vertical slider   -- translate up-down
    x field -- translate in x axis
    y field -- translate in y axis

  SCALE
    vertical slider -- scale larger-smaller
    % field -- scale in percent

Clicking in the trough on either side of the
slider makes a small movement (one unit).  A
ctrl-click in the trough moves all the way to the
end of the scale (e.g., to quickly do a 180
flip).

The pose can be more quickly changed by direct
manipulation (Shift-click drag).

The "winxy:" entry in the ROTATE panel can be use
to change/return the display window to a known
size (default startup size: 600x600 pixels).  It
can't be larger than the current screen.  See
R-click help to the "rgb:" entry for instructions
on how to render very high resolution bitmaps
offscreen.

Use the "Save" button (inside the TRANSLATE
panel) to save one particular pose, which can be
restored at any time with the "Goto" button.  The
previous saved pose is overwritten with each new
"Save".  Middle-click "Save/Goto" to add a
suffix.  Click "View" to see currently saved.

The DIST button in the TRANSLATE panel calculates
the Euclidean distance between the last 2 clicks
on the surface and displays the result in the
log.  The "Dijk" button calculates a geodesic
distance and generates a label of the path.

The "isocont" tick below the DIST button reads
$hemi.sphere.reg (if there) in order to display
isocontours of canonical spherical coordinates on
top of any current data (Right-click on "isocont"
to get more details).

-------------------------------------------
RIGHT COLUMN OF TKSURFER INTERFACE
-------------------------------------------

REDRAW/RESTORE/SEND/GOTO [F3: FlipHemi]

The REDRAW button manually redraws the screen.
This is rarely needed now, since most interface
changes do an auto-redraw on clicking a button or
hitting Return after adjusting a value in an
entry.  Alternate clicks on REDRAW (middle- and
shift-middle) display divergent or convergent
stereo (plain left-click REDRAW to undo either).

RESTORE reestablishes the original, lateral
viewing orientation and scale (it is easy to get
lost after several non-orthogonal rotations).  An
alternate click re-calculates center.

SEND saves the coordinates of a surface point in
a file that can be read by tkmedit GOTO in order
to display the 3D slice position of that vertex.
The folded surface position of the vertex is used
(regardless of which surface is being viewed).

GOTO finds the nearest (Euclidian distance)
surface point to the last point that was saved by
a SEND or SEND PNT in surfer tools, medit tools,
or tkregister.  This can be used in tksurfer to
return to a vertex on the same (or different)
surface.

FlipHemi (F3 interface) fixes surface read from
software that uses a left-handed coordinate
system (e.g., Brain Voyager).

Note that the GOTO operation is not equivalent
for surface (2D) and volume (3D) data sets.  Any
surface point can always be located (to within a
voxel) in a volume data set; however, a point
selected in a volume often lies outside of the
corresponding cortical surface, so a search must
be performed.  This can lead to counterintuitive
results.  For example, assume you are displaying
the right hemisphere surface.  If a volume point
is accidentally selected in the opposite (left)
hemisphere of a 3D data set, and you GOTO that
point on the surface the nearest (Euclidean)
surface point will typically lie somewhere on the
midline, and will not even be visible in lateral
view.

Colors: ("Col:")

If "curv" is unticked, the curvature dataset is
not displayed.  The default is ticked.  If
curvature is displayed without an overlay, it is
represented using red and green.  When curvature
is displayed under activations ("overlay"
checked), curvature is represented as two shades
of gray.  The overall brightness of the
dark/light gray background can be controlled with
the first entry to the right of "curv" (variable:
offset, default=0.30).  The second entry controls
the contrast between the two shades of gray
(variable: cvfact, default=1.30).

"intp", "bot", and "sym" ticks

These control how the entries in a color look up
table (LUT) are interpreted:

  intp -- interpolate between entries
  bot  -- entry specifies bottom (not center)
  sym  -- thresh cuts in symmetrically around 0

Color Scales: ("w1", "ht", etc radio buttons)

  w1  -- color wheel (complex data, pol or ecc)
  ht  -- heat (brown->white, complex amplitude)
  bi  -- red/grn (complex data, sign from phase)
  BRy -- blue/red/yellow (signed real data)
  BR  -- blue/red/white (signed real data)
  lt  -- color look up table ($val2rgblut)
  fs  -- fieldsign ($hemi.fs masked by $hemi.fm)

For much more detail on the behavior of these
different color scales, see SessionTools -> View
Functional Data or their R-click help popups.
Left-click "Col:" to get a more verbose
radiobutton popup with all the color scales (all
have R-click help).

MESH and "rect","cm" Buttons

MESH toggles between viewing and not viewing the
vertices and edges of each polygon in the
surface.  The mesh is too dense to resolve unless
the brain is first SCALE'ed up.  The r/g/b fields
can set the color of the mesh.  Update mesh color
by changing r/g/b and typing Return.

The r/g/b fields are also used to specify what
color a label will be be displayed with.  To
change the color of a label, reset r/g/b and
re-display the label with "D".

The "rect" button toggles between a rectangular
and circular display of different phases in the
color disk inset at the lower right (only
relevant for the color disk, which is displayed
with complex-valued color scales).  The circular
display is more familiar; the rectangular display
also illustrates the effects of changing fthresh
and fmid.  With polar angle data (non-integral
angle_cycles), only 180 deg of phase are
displayed.  A third click toggles off the inset.

The "cm" button toggles the visibility of both:
(1) the color disk inset if complex (or vertical
colorbar if non-complex) and (2) a 1 cm
horizontal gray scale bar at the lower right.

Display Parameters (middle part of tksurfer right column)

Left SubColumn Ticks and "fthresh:" Entry Field

 truncphase -- only display positive values
   => phase limits settable if complex-valued
 invphase   -- reverse positive/negative values 
 ovlay      -- display functional data (gray curv)
 cpx        -- complex-valued data (usu. autoset)
 mask       -- mask amplitude data by stat
 mult       -- multiply amp data by stat
 fthresh -- hard stat thresh (stat values under
              this value display underlying gray
              curvature instead)

Middle SubColumn Box -- phase contours

Ticking "phc" displays isophase contours whose
number and width are controlled with the "n" and
"w" entries immediately below.  The sharp edge of
each phasecontour points in the direction of the
phase gradient (steepest uphill direction).

The unmarked tick and entry at the bottom of this
box control gradient arrow displays (see R-click
help for "GR" button).

Right SubColumn Entry Fields

 fslope -- contrast (steepness sigmoid, or double
             sigmoid steepness w/2-ended scales)
 fmid   -- set where sigmoid changes fastest (use
              this to set effective threshold
              but avoid truncation edges)
 cslope -- contrast of R/G curvature colorscale
 cmid   -- midpoint of R/G curvature colorscale
 iy     -- ipsiyellowfact (0-1) for polar angle
 fad    -- fadef (~0.7) ipsi fade for polar angle
 anglecycles -- complex: eccen=1.0, polar ~2.2
 rev    -- reverse time axis (complex data)
 angleoffset -- complex: 0-1 => phaseangle 0-2pi

If "mask" is ticked (and a statistical mask has
be read in), the fthresh (hard thresh), fslope
(contrast), and fmid (soft thresh) fields are
each doubled.  The first set in each pair uses
the amplitude (e.g., cut out vertices with low
amplitudes) and the second set uses the
statistical mask (e.g., cut out vertices with low
statistic).

For more detail on the meaning of these fields as
a function of different color scales, see
SessionTools -> View Functional Data.

Surface Cut/Fill/Roi Operations

  AREA -- closed-line cut using most recent
             list of LEFT-CLICK'd points
  FILL -- fill region selected by single
            LEFT-CLICK, then delete unfilled
  PLANE -- 3 LEFT-CLICK's define plane, 4th
                indicates which side to keep  
  LINE -- open-line cut using most recent
               list of LEFT-CLICK'd points
  ROI -- fill connected region with val above
            fthresh (complex->use complex amp)
  N -- select <n> vertices closest to curr click
  A -- select nearest vertices up to area (sqmm)
  R -- select nearest vertices up to radius (mm)
  UNDO -- undo most recent cut
  INIT -- reset surface to initial state read in
  ALL -- undo all cuts, even initially read

Once an operation has been performed, the current
list of selected/marked points is cleared in
preparation for the next operation.  You can
clear the marked list at any time with a
right-click.  Click "erode" to make FILL perform
an eroding fill (see FILL right-click help for
more details).

Data and Curvature Smoothing

The SMOOTH button performs the requested number
of nearest-neighbor smoothing steps on either the
curvature ("cv") or the data on the top of the
value stack ("val") if real, or on both real and
imaginary components if complex.  The approximate
mapping from smoothing steps to full width at
half max 2D Gaussian filter is:

   5 steps => 2.2 mm FWHM
  10 steps => 3.2 mm FWHM
  20 steps => 4.5 mm FWHM

Because this is a 2D filter, a given kernel size
will result in less smoothing than a 3D kernel of
the same size.

Two additional rarely used smoothing types are:
"w" (weighted), which uses $halfweightdist
(0.5*FWHM), the distance in mm where the Gaussian
weight drops to 0.5, and "sp" (sparse), which
ignores vertices without data and interpolates
across them at the 'speed of light' (one vertex
per smoothstep, which will have no effect on full
rank data).

See Help -> View Functional Data for more details
on smoothing (also, right-click help for these
radio buttons).

Finally, alternate middle and shift-middle clicks
on the SMOOTH button make popups to control
searchlight smoothing operations (C/tcl funct
searchlightop_val2stat) and and the generation of
non-overlapping 'paintball' regions (C/tcl funct
neighop_val2stat).

-------------------------------------------
 Standalone Commandline and Script Operation
-------------------------------------------
tksurfer can also be run as a standalone program
from the command line (as long as SUBJECTS_DIR and
FUNCTIONALS_DIR are set):

Use: [disp surf w/curvature and/or stats overlay]

 tksurfer [+]name hemi surf [-tcl script] [-opts]
 tksurfer -help,-h ["help" at prompt, R-click item]

  name:   subject (+ prefix => read T1.[mgz])
  hemi:   lh, rh
  surf:   orig,smoothwm,inflated,sphere,sphere.reg
  tclscript:  first local, then $CSURF_DIR/lib/tcl

The program has an embedded tcl interpreter and
most of its C functions (including some not
exposed to the surfer tools interface) can be
accessed through it, either interactively at the
tcl prompt, %, that appears in the terminal from
which tksurfer is started, or offline using a tcl
script in a file whose name follows the -tcl
option.  If you include an "exit" at the end of
the tcl script, tksurfer will quit when the
script finishes.  You can find commands available
in the tcl interpreter using "help":

  % help

which also lists settable variables and commands.
To find tcl variables and commands, use:

  % info vars
  % info commands

Set the value of a variable like this:

  % set cslope 5.0

To find the current value, omit the value

  % set cslope
  3.0

You can also search for commands by pattern
matching:

  % info command rotate*
  rotate_brain_x rotate_brain_y rotate_brain_z

Commands have some minimal error checking/usage:

  % rotate_brain_y
  Wrong # args: rotate_brain_y <deg>

Unix shell commands can be executed directly from
the tcl prompt (but to get csh-style globbing,
you have to do it explicitly):

  % cd /tmp
  % ls -al
  % eval ls -al [glob *.rgb]

Here is an example of a small command line tcl
script that reads in a surface, and then saves 90
bitmap images of the brain, each one rotated 4
degrees around the vertical axis from the
previous image (i.e., a rotating brain movie).

open_window
make_lateral_view
read_binary_curv
set cslope 5.0
set i 0
while {$i < 90} {
  redraw
  set rgb /tmp/brain-[format "%03d" $i].tiff
  save_rgb
  incr i
  rotate_brain_y 4
}
exit

You could either paste the script into the
tcl prompt or put the commands in a file,
rotbrain.tcl, run it like this:

  tksurfer john rh smoothwm -tcl rotbrain.tcl

Finally, you could also execute the script from
the surfer tools interface by entering its name
in the "tcl:" entry and clicking "GO".  The name
in the entry can be: (1) the full pathname to the
script, (2) if the script is put in the scripts
dir of the current session, just its relative
name, or (3), if the script has been put in the
standard scripts directory, using a "#" as an
abbreviation for the standard scripts dir.

-------------------------------------------
 C-code for machine-independent MSB read/write
-------------------------------------------

/*** global */
#ifdef Darwin
int LSBfirst = 1;   /* runtime endian flag */
#endif

/*32bit: int=4, long=4, long long=8, float=4, double=8*/
/*64bit: int=4, long=8, long long=8, float=4, double=8*/

/*** typdefs */
typedef union
{
  short  s;
  char   buf[sizeof(short)];
} SWAP_SHORT;

typedef union
{
  float  f;
  int    i;
  unsigned int  ui;
  char   buf[4];
  short  s[2];
} SWAP_FLOATINT;

/*** function declarations */
void setdarwinbyteorder(void);
short swapShort(short s);
int swapInt(int i);
unsigned int swapUInt(unsigned int ui);
long long swapLong(long long l);
float swapFloat(float f);
double swapDouble(double d);
int fread1(int *v, FILE *fp);
int fread2(int *v, FILE *fp);
int fread3(int *v, FILE *fp);
int fread4(float *v, FILE *fp);
short freadShort(FILE *fp);
int freadInt(FILE *fp);
unsigned int freadUInt(FILE *fp);
long long freadLong(FILE *fp);
float freadFloat(FILE *fp);
double freadDouble(FILE *fp);
int fwrite2(int v, FILE *fp);
int fwrite3(int v, FILE *fp);
int fwrite4(float v, FILE *fp);
int fwriteShort(short s, FILE *fp);
int fwriteInt(int v, FILE *fp);
int fwriteLong(long long v, FILE *fp);
int fwriteFloat(float f, FILE *fp);
int fwriteDouble(double d, FILE *fp);

/*** functions */
void setdarwinbyteorder(void)
{
#ifdef Darwin  /* else no-op */
  const NXArchInfo *archinfo;

  archinfo = NXGetLocalArchInfo();
  if (archinfo->byteorder==NX_BigEndian)
    LSBfirst = 0;
  else if (archinfo->byteorder==NX_LittleEndian)
    LSBfirst = 1;
  else {printf("### NXGetLocalArchInfo fail\n");}
#endif
}

short swapShort(short s)
{
  char        c;
  SWAP_SHORT  ss;

  ss.s = s;
  c = ss.buf[0];
  ss.buf[0] = ss.buf[1];
  ss.buf[1] = c;

  return(ss.s);
}

int swapInt(int i)
{
  short          s;
  SWAP_FLOATINT  sfi;

  sfi.i = i;
  sfi.s[0] = swapShort(sfi.s[0]);
  sfi.s[1] = swapShort(sfi.s[1]);
  s = sfi.s[0];
  sfi.s[0] = sfi.s[1];
  sfi.s[1] = s;
 
  return(sfi.i) ;
}

unsigned int swapUInt(unsigned int ui)
{
  short          s;
  SWAP_FLOATINT  sfi;
 
  sfi.ui = ui;
  sfi.s[0] = swapShort(sfi.s[0]);
  sfi.s[1] = swapShort(sfi.s[1]);
  s = sfi.s[0];
  sfi.s[0] = sfi.s[1];
  sfi.s[1] = s;

  return(sfi.ui);
}

long long swapLong(long long l)
{
  char    *pVar=(char *)(&l);
  char    tmp;
  size_t  typeSize=sizeof(long long);
  size_t  i;
  long long  w;  /*double  w;*/

  for (i=0; i<typeSize/2; ++i) {
    tmp = *(pVar + 2*i);
    *(pVar + 2*i) = *(pVar + typeSize - 1 - 2*i);
    *(pVar + typeSize - 1 - 2*i) = tmp;
  }
  w = *((long long *)(pVar));
  return w;
}

float swapFloat(float f)
{
  short          s;
  SWAP_FLOATINT  sfi;

  sfi.f = f;
  sfi.s[0] = swapShort(sfi.s[0]);
  sfi.s[1] = swapShort(sfi.s[1]);
  s = sfi.s[0];
  sfi.s[0] = sfi.s[1];
  sfi.s[1] = s;

  return(sfi.f);
}

double swapDouble(double d)
{
  char    *pVar=(char *)(&d);
  char    tmp;
  size_t  typeSize=sizeof(double);
  size_t  i;
  double  w;

  for (i=0; i<typeSize/2; ++i) {
    tmp = *(pVar + 2*i);
    *(pVar + 2*i) = *(pVar + typeSize - 1 - 2*i);
    *(pVar + typeSize - 1 - 2*i) = tmp;
  }
  w = *((double *)(pVar));
  return w;
}

{
  unsigned char  c;
  int            ret;

  ret = fread(&c,1,1,fp);
  *v = c;
  return(ret);
}

int fread2(int *v, FILE *fp)  /* short */
{
  short  s;
  int    ret ;

  ret = fread(&s,2,1,fp);
#ifdef Linux
  s = swapShort(s);
#endif
#ifdef Darwin
  if (LSBfirst) s = swapShort(s);
#endif
  *v = s;
  return(ret);
}

int fread3(int *v, FILE *fp)  /* unsigned int */
{
  unsigned int  i=0;
  int           ret;

  ret = fread(&i,3,1,fp);
#ifdef Linux
  i = (unsigned int)swapInt(i);
#endif
#ifdef Darwin
  if (LSBfirst) i = (unsigned int)swapInt(i);
#endif
  *v = ((i>>8) & 0xffffff);
  return(ret) ;
}

int fread4(float *v, FILE *fp)  /* float */
{
  int    ret;
  float  f;

  ret = fread(&f,4,1,fp);
#ifdef Linux
  f = swapFloat(f);
#endif
#ifdef Darwin
  if (LSBfirst) f = swapFloat(f);
#endif
  *v = f;
  return(ret);
}

short freadShort(FILE *fp)
{
  int    nread;
  short  s;

  nread = fread(&s,sizeof(short),1,fp);
#ifdef Linux
  s = swapShort(s);
#endif
#ifdef Darwin
  if (LSBfirst) s = swapShort(s);
#endif
  return(s);
}

int freadInt(FILE *fp)
{
  int  i, nread;

  nread = fread(&i,sizeof(int),1,fp);
#ifdef Linux
  i = swapInt(i) ;
#endif
#ifdef Darwin
  if (LSBfirst) i = swapInt(i);
#endif
  return(i);
}

unsigned int freadUInt(FILE *fp)
{
  unsigned int  i;
  int           nread;

  nread = fread(&i,sizeof(int),1,fp);
#ifdef Linux
  i = swapUInt(i);
#endif
#ifdef Darwin
  if (LSBfirst) i = swapUInt(i);
#endif
  return(i);
}

long long freadLong(FILE *fp)
{
  int        nread;
  long long  i;

  nread = fread(&i,sizeof(long long),1,fp);
#ifdef Linux
  i = swapLong(i);
#endif
#ifdef Darwin
  if (LSBfirst) i = swapLong(i);
#endif
  return(i);
}

float freadFloat(FILE *fp)
{
  float  f;
  int    ret;

  ret = fread(&f,4,1,fp);
#ifdef Linux
  f = swapFloat(f);
#endif
#ifdef Darwin
  if (LSBfirst) f = swapFloat(f);
#endif
  return(f);
}

double freadDouble(FILE *fp)
{
  int     ret;
  double  d;

  ret = fread(&d,sizeof(double),1,fp);
#ifdef Linux
  d = swapDouble(d);
#endif
#ifdef Darwin
  if (LSBfirst) d = swapDouble(d);
#endif
  return(d);
}

int fwrite2(int v, FILE *fp)  /* short */
{
  short  s;

  if (v > 0x7fff)    /* don't let it overflow */
    v = 0x7fff;
  else if (v < -0x7fff)
    v = -0x7fff;
  s = (short)v;
#ifdef Linux
  s = swapShort(s);
#endif
#ifdef Darwin
  if (LSBfirst) s = swapShort(s);
#endif
  return(fwrite(&s,2,1,fp));
}

int fwrite3(int v, FILE *fp)  /* unsigned int */
{
  unsigned int  i=(unsigned int)(v<<8);

#ifdef Linux
  i = (unsigned int)swapInt(i) ;
#endif
#ifdef Darwin
  if (LSBfirst) i = (unsigned int)swapInt(i) ;
#endif
  return(fwrite(&i,3,1,fp));
}

int fwrite4(float v, FILE *fp)  /* float */
{
#ifdef Linux
  v = swapFloat(v);
#endif
#ifdef Darwin
  if (LSBfirst) v = swapFloat(v);
#endif
  return(fwrite(&v,4,1,fp));
}

int fwriteShort(short s, FILE *fp)
{
#ifdef Linux
  s = swapShort(s);
#endif
#ifdef Darwin
  if (LSBfirst) s = swapShort(s);
#endif
  return(fwrite(&s,sizeof(short),1,fp));
}

int fwriteInt(int v, FILE *fp)
{
#ifdef Linux
  v = swapInt(v);
#endif
#ifdef Darwin
  if (LSBfirst) v = swapInt(v);
#endif
  return(fwrite(&v,sizeof(int),1,fp));
}

int fwriteLong(long long v, FILE *fp)
{
#ifdef Linux
  v = swapLong(v);
#endif
#ifdef Darwin
  if (LSBfirst) v = swapLong(v);
#endif
  return(fwrite(&v,sizeof(long long),1,fp));
}

int fwriteFloat(float f, FILE *fp)
{
#ifdef Linux
  f = swapFloat(f);
#endif
#ifdef Darwin
  if (LSBfirst) f = swapFloat(f);
#endif
  return(fwrite(&f,sizeof(float),1,fp));
}

int fwriteDouble(double d, FILE *fp)
{
#ifdef Linux
  d = swapDouble(d);
#endif
#ifdef Darwin
  if (LSBfirst) d = swapDouble(d);
#endif
  return(fwrite(&d,sizeof(double),1,fp));
}
