################################################################### UPDATE: 30 Sep 2026 ################################################################### ------------------------------------------------------------------- Summary: (1) ARM compile for MacOS 26 (Tahoe) w/AFNI (2) calculate/display rawdata time-series covariance (3) sample flat patch .val data to x-y grid and back ------------------------------------------------------------------- ------------------------------------------- (1) ARM compile for MacOS 26 (Tahoe) ------------------------------------------- The Mac upgrade from MacOS 25 (Sequoia) to MacOS 26 (Tahoe) broke all X11/GLX/OpenGL apps running on XQuartz (e.g., glxgears). Intrepid programmers (Peter Molfese, above my pay grade) tracked down some of the problems and prepared a homebrew 'tap' of work-around X11/GLX/GL libraries (mesa25.3.4 - N.B.: early mesa26 regressed to broken on Tahoe, fixed on later mesa26). I compiled all the csurf programs on Tahoe against his fixed libs, so tksurfer, etc, should now run (tested up to Tahoe/macOS 26.7.1): https://pages.ucsd.edu/~msereno/csurf/csurf0.8-arm-260930.tgz To avoid having to unquarantine a downloaded tarfile on Mac, here is how to download and unpack in one step: cd curl -sSL 'https://pages.ucsd.edu/~msereno/csurf/csurf0.8-arm-260930.tgz' | tar xfz - To recursively unquarantine an already unpacked csurf, do: cd sudo xattr -r -d com.apple.quarantine csurf The arm64 distribution includes the fixed X11/GLX/GL mesa libraries within it, so it should just work on Tahoe, even with a broken XQuartz and without homebrew installed. Just cd into the unpacked csurf and first run FreeSurferEnv.{c}sh as usual, so that csurf apps can find the fixed X11/GL libraries. This distribution contains AFNI bins used by csurf. On Mac, this is macos_10.12_local (x86_64), which I tested runs on ARM on MacOS 26/Tahoe w/Rosetta2. If this AFNI doesn't work for some reason, you can link csurf to your working AFNI with: cd csurf # wherever you installed it mv afni afni~ # just in case mkdir -p afni/bin # make a fresh dir visible to csurf cd afni/bin # cd into it ln -s Darwin-arm64 # make link to yours N.B.: The arm64 Tahoe distribution has only been tested on apple silicon running Tahoe, so I don't know if it will run on ARM/Sequoia or earlier OS's. If you have any info on this, please email me! N.B.: The arm64 Tahoe distribution *won't* work on an Intel machine running Tahoe (these are arm64-only binaries). I don't yet have -arch x86_64 csurf/library compiles working on my ARM/Tahoe machine, and unfortunately, I don't have access to an Intel/Tahoe machine for testing. ------------------------------------------- (2) calculate/display rawdata covariance ------------------------------------------- New (very) basic functionality to calculate and display rawdata time series covariance has been added. There are 4 new C/tcl functions: calc_rawdata_covar write_rawdata_covar read_rawdata_covar copy_rowcovar_val These functions can be accessed via the buttonbar from a R-click on the "X" (cross-corr) button on the "label:" line. The first step is to set up a new functional session with a scandir containing a raw time series data BRIK and register that rawdata to a subject in the standard way (makes register.dat). Then read the rawdata BRIK into tksurfer using the alternate set of 3D buttons on the "val:" line: (1) click "val:" to toggle it to "val3d:" (2) click "RD" to get rawdata popup (3) select rawdata BRIK from popup "val3d:" dropdown (4) click "READ RAWDATA and REGISTRATION" Next, click "label:" to get NormSamp controls and set the sampling method and sampling point, using either mm or fraction of cortical thickness: if normdfracflag unticked (mm above orig GM/WM surface), set normdsamp, normdmax equal (e.g., 1.0 [mm]) if normdfracflag ticked (fraction of cort thickness above GM/WM), set normdfracsamp, normdfracmax equal (e.g., 0.6 [=60%]) Next, R-click the "X" button on the "label:" line to get a button-bar popup, and click "Calculate RawData Covariance" on the popup (there will be an error if you are sampling a range, not a point, along the surface normals). This procedure first finds 'representative' or 'unique' vertices. That is, for each sampled 3D voxel, find the surface vertex closest to the 3D positional average of all the vertices that sampled that voxel (to avoid having duplicate entries in the covariance matrix). N.B.: to see the 'representative' vertices, click "UQ" on the "val3d:" line after "Calculate RawData Covariance" has run. The C/tcl functions run by "Calculate RawData Covariance" are: rawdatasubbrik_to_paintable 0 # extract first frame of rawdata paint_surface 0 # 'paint' to surf to find sample voxels find_uniqsamp_vertices # find voxel-unique 'representative' vtxs calc_rawdata_covar # calculate covariance matrix N.B.: you can do the first three operations manually one at a time from the tksurfer interface as follows: (1) click "MAKE FIRST VOL PAINTABLE (for uniq)" on popup (2) click "PA" button on "val3d:" line (does paint) (3) click "UQ" button on "val3d:" line (does unique vertices) (these will be harmlessly redone by "Calculate RawData Covariance"). Each covariance matrix entry (for a pair of vertices, v1 and v2) is computed in the usual way as follows (sum is across t): Sum[(v1 - v1_avg) * (v2 - v2_avg)] corr = ------------------------------------- n - 1 which results in a symmetric matrix with variances along the diagonal. To view individual rows of the covariance matrix, click "Display Correlation for CurrVertex" on the Label "X" popup, after selecting a 'seed' vertex (the actual seed will be the 'representative' vertex closest to your selected vertex). The first time this runs, the colscale will be set to sane values to display the correlations: set overlayflag 1 set truncphaseflag 0 set complexvalflag 0 set statmaskampflag 0 set colscale 11 set fslope 0.1 set fthresh 5.0 set fmid 10.0 Since the (large) covariance matrix (about 13K by 13K) only contains 'representative' vertices, the correlation data is first interpolated on the surface with smooth_val_sparse (the "sp" option for the SMOOTH button). During this smoothing, the values at 'representative' vertices are fixed and only the undefined vertices in between are interpolated (20 steps). Finally, to export the covariance matrix to the current scandir to analyze it with external tools, use the "Write RawData Covariance" button on the R-click "X" popup. This will write out two files in the current scandir with standard names. The first file: $hemi-CovarianceMatrix.float is the covariance matrix, written as a string of floats in standard freesurfer byte order (MSB). Use AFNI 4swap to convert to Intel (LSB) byte order to read the binary data into an external program, for example: 4swap rh-CovarianceMatrix.float The indices of the rows and columns of this matrix are sequentially numbered 'representative' vertices, which are a subset of full vertex subset. So, in addition to the covariance matrix, an ASCII file of the original full surface vertex numbers of these 'representative' vertices is also written to: $hemi-CovarianceMatrixVnums.txt Since it takes a minute to calculate the large covariance matrix, you can read a previously calculated matrix into tksurfer with "Read RawData Covariance" on the R-click "X" buttonbar popup. Finally, a new popup to select/query vertices by vertex number and dispay time series is available by R-clicking the REDRAW button and clicking the bottom "Select Vertex By Number Popup" button (mainly for debugging). The buttons are: CURRENTLY SELECTED VTX (from display window click) SELECT VERTEX BY NUMBER SHOW VERTEX TIMECOURSE SHOW UNIQSAMP VERTICES ------------------------------------------- (3) sample flat patch .val data to grid and back ------------------------------------------- new popup: from R-click on "W" button on "label:" line new C/tcl functs: write_flatval_to_grid read_gridval_to_surf ---------------- Surface -> Grid: ---------------- The first button on the popup allows sampling vertexwise .val data that is currently loaded on a flat surface to a regular x-y grid -- e.g., to process it with an external program expecting a grid. The grid size is set by the $gridmm entry (default value 0.6mm similar to average surface edge size). The bounding box of the grid is calculated from min/max values of any displayed vertices. For each vertex, the .val at that vertex is assigned to the closet grid point. The grid of resampled .val's is written out as a headerless freesurfer MSB (most significant byte first) 32-bit float binary file with a standard hemisphere-specific name to the subject's label directory: $hemi-ValToGrid.MSBfloat In order to be able to read the grid data (possibly modified), back onto the same flat surface (see bottom button on popup), a second ASCII/text file is written containing the surface vertex numbers closest to each grid point: $hemi-ValToGridNearest.txt This file is written in the same order as the binary MSB float file (x is the inner loop). Its first line is a "#" comment, which contains the subject, patch, x and y grid dimensions and the grid spacing (gridmm). The sampling process can result in holes in the grid since each point in the surface is assigned to only one grid point. Ticking $fillholesflag causes a (slower 1-2min, multithreaded) search, where grid holes are filled with the .val from the closest vertex, as long as it is less than $maxdatadistmm (default=1.5mm) away from the grid point hole. N.B.: use AFNI 4swap to convert freesurfer MSB float files to LSB float files for processing in external programs. ---------------- Grid -> Surface: ---------------- The second button on the popup allows sampling a regular x-y grid of MSB float .val data onto a flat surface. The float data file (and the nearest vertex ASCII file) will previously have been written out by the C/tcl function, write_flatval_to_grid (SAMPLE SURF VAL TO GRID FILE button on the popup). The two hemisphere specific input files have standard names, and are read from the subject's label directory: $hemi-ValToGrid.MSBfloat $hemi-ValToGridNearest.txt The float data may have been subsequently modified by an external program before reading it back onto the flat surface for viewing. For each grid point, vertex, the .val at that point is assigned to the vertex number for that grid point found in the second ASCII input file. The currently displayed subject and flat surface must match what is found in the first "#" comment header line in the $hemi-ValToGridNearest.txt file. The sampling process can result in holes on the surface since each point in the grid is assigned to only one surface vertex. Ticking $fillholesflag causes a (slower 1-2min, multithreaded) search, where surface holes are filled with the .val from the closest grid point, as long as it is less than $maxdatadistmm (default=0.5mm for a 0.6mm grid) away from the surface vertex hole. N.B.: to view the data on a different (e.g., inflated_avg) surface, just switch to any other surface in tksurfer after reading in the grid data. N.B.: use AFNI 4swap to convert between LSB (most Intel/ARM) and MSB (freesurfer) float file files. ----------------------------------------------- Bug fixes, small changes ---------------------------------------------- tksurfer interface stays up even if no GLX win (all computation works) draw areanames now respects annotmaskflag (show subset of areas) UQ popup reports sampvtxcnt, uniqvtxcnt, vtxs per voxel NormSamp popup: enable/disable relevant parms: normdfracflag, bokflag new R-clk-REDRAW accessible "Select Vertex by Number Popup" ########################################################################## UPDATE: 30 Sep 2026 ########################################################################## ---------------- 30Sep2026 csurf0.8 tarballs ------------------------------ --csurf: sphavgcxcmd,volavgcxcmd: $use3dttestplusplusflag: 3dttest++ vs 3dttest --csurf: sphavgcxcmd,volavgcxcmd - if $use3dttestplusplusflag, use 3dttest++ --csurf: startup: 3dttest notfound lookfor 3dttest++, set use3dttestplusplusflag --mk0: update 'mk0 afni' tarfile fish out: get 3dttest++ if no 3dttest --mk0: Rosetta2/ARM: add 10.12_local libs precompiled, libs -> afni bin --mk0 (04s): update afni download to current state afni site --tksurfer.c: draw_areanames() now respects annotmaskflag (show subset of areas) --tkmedit.c: make linearflag respect contrast reversed (bwslope is negative) --brik2cor.c: add invthresh to skip inverting almost-zeros --tkmedit.c: HOR/SAG TRUNC to real 0 (=COR), not sqmap[0] (only vis midpt<0.15) --tksurfer.tcl: if not openglwindowflag say non-graphics cmds still work --tksurfer.c: don't exit if can't open glx window (non-graphics cmds still work) --tksurfer.c: rename read_gridval_to_surf -> read_gridval_to_flat symm. w/write --tksurfer.c: fillpix updated 1.0 -> 0.5 on read 512^3 dateset [tmprelease 260825] --tksurfer.c: read_gridval_to_surf requires same flat patch and subject --tksurfer.c: require same flat patch on read grid to surface --lib/help/tksurfer/label_{write,read2val}: update --tksurfer.tcl: write/read grid now put up same popup, with two offset helpwins --wrappers.tcl: helpwin early exit/raise returns "raise" instead of window name --tksurfer.c,tcl: new multithread read_gridval_to_surf fillholes/maxdatadistmm --tksurfer.c,tcl: rm broken read_gridval_to_surf fillholes (was onlynearvtx=0) --tksurfer.c: multithread fillholesflag option in write_flatval_to_grid 8m->2m --tksurfer.tcl: add labelR_grid (runs read_gridval_to_surf (also prev popup) --tksurfer.tcl: samp2gridctrls/do_samp2grid (runs write_flatval_to_grid) --tksurfer.tcl: checklabelfile bug fails on some 5,6 args label (how long?!) --tksurfer.c: compute_avg_edgewidth() skips border/ripped esp. better flat est. --tksurfer.c: add/export: write_flatval_to_grid(), read_gridval_to_surf() --mk0: 04r: action "clean", libtool vs ranlib for strtod fix XC12 on Catalina --mk0: 04q: strtod ranlib fix Xcode12 ahead-load Catalina vs. BigSur+ -> faile --mk0: 04p: insttmp edits FreeSurferEnv.{sh,csh} to copy state in mk0 --mk0,mk,getonearch,Makefile: systbrew->optloc, csurfbrew->csurfloc --FreeSurferEnv.{sh,csh}: v18: add $uselocalx11gl parallel to mk0 --csurf: chk ./tardate-YYMMDD.txt to set $tarfiledate if notfound in README.txt --mk0: save second copy of tardate file to $srcdir --mk0: "mk0 csurf" reports commands that will run, "mk0 dist" explains itself --mk0: compile/link mesa26 software render on 10.15 -> fail (still Frameworks!) --mk0: change 'mk0 onearch' etc to more sensible 'mk0 csurf' --mk0: add $uselocalx11gl ($macxllgl:systbrew->csurfbrew (brew cp in csurf)) --csurfsrc/dist/precompiled/X11GL/include/Darwin-: find min req'd X11 incl --getonearch,mk: cleanup,doc,overrides: mk0->mk->getonearch->exportvars->make --tksurfer.tcl: CS buttonbar: title correction: Val "R" -> Val "CS" --update-csurf: download and untar at once (avoids Mac Gatekeeper quarantine) --FreeSurferEnv.{c}sh: Darwin-arm86->Darwin-arm64 :-} (fixed 260622 tarfiles) [tmprelease 260622] --tksurfer.c: accept/ignore -fontfile if gcc/no-font so won't quit w/bad opt --tksurfer.c: raise mup/etc+=0.01 to fix label holes in Linux VirtualBox --mkcsurflist,update-csurf: now recognize csurf0.8-arm-YYMMDD.tgz ------- MacOS 26 Tahoe (release=25) compile working --------- --FreeSurferEnv.{c}sh: v17: cleanup old, align sh/csh versions --mk0: add new mpeg_{play,encode} patches for Tahoe --mk0: arm64 insttmp adds precompiled mesa-25.3.4 libs $CSURF_DIR/X11GL --mk0: dist incl's 60M precompiled mesa-25.3.4: dist/precompiled/X11GL --mk0: MacOS 26 Tahoe compile working w/precompiled brew --{tkmedit/tkregister/tkstrip}.c -- clean up "unsed but set" warnings --Makefile tkregister: ifeq(${macCGLparms),1) for optional gcc ... -DmacGLparms --mk: use getonearch to export $macCGLparms 0|1 (CGL parms gone post-Catalina) --getonearch: also report 0|1 for $macCGLparms (CGL parms gone post-Catalina) --tkregister.c: doublebuffer *or* singlebuffer OK --tkregister.c: #ifdef macCGLparms, include CGLSetParameter swapInterval --tkregister.c: MacOS 26 (Tahoe): CGLSetParameter swapInterval no longer exists --tkregister.c: CGLSetParameter swapInterval req'd up to at least MacOS 10.15 --tkmedit.c: doublebuffer *or* singlebuffer OK/equivalent --Makefile tkmedit/tkregister/tkstrip: same lib/incl logic as Makefile tksurfer --tksurfer Makefile: add $macx11gl (xquartz vs. systbrew vs. csurfbrew) logic --mk: get $macx11gl from getonearch, export it to makefile env --getonearch: can now output $macx11gl, proc arm -> $oneproc=arm64 (was x86_64) --tksurfer.c: fix many 'set but not used' and 'misleading indentation' warnings --tksurfer.c: fix samevtxthickflag no-op in move_along_normal,bug intro'd Jun'24 --tksurfer.tcl: harmonize read_{mgh,poi,obj,gifti}_to_annot set transparency --tksurfer.c: read_{gifti,obj,poi}_to_annot: init avgx,y,z (drawnames crashers) --tksurfer.c: read_{gifti,obj,poi}_to_annot: forgot find_regionvtx_nearest_avg --tksurfer.c: add getkernelreleasenum(),override doublebufferflag Tahoe+/kern>24 --mk0: mk0 now gives error (vs. silence) ftgl: use homebrew GL libs (same as GLX programs, tksurfer etc), patch demo tiff/ezxml/freetype: worked as is tix: use consistent (e.g., homebrew) X11 (not just GL) libs tix: awk "#include " to generic/tix{GrUtl,ImgXpm}.c for isspace tix: X11 link to *tk* dir already on tix -L path: fix no find -lX11 Mac tk/tix: use same X11 libs as libftgl, GLX progs (tksurfer etc) tk: use consistent (e.g., homebrew) X11 (not just GL) libs tk: patch missing _fixstrtod: add fixstrtod.o to Makefile GENERIC_OBJS tk: patch missing _fixstrtod: cp tcl-compiled fixstrtod.o into tk tcl: ranlib strtod.o, fixstrtod.o to fix dup'd symbols tcl: typedef int->long ptrdiff_t tclInt.h fix tclUnixTime.c redef xc12.4+ use tcl8.5/tk8.5/tix8.4 (vs. 8.6) for less interface adjust, less tix patch --mk0: add $kernelreleasenum, homebrew mesa-25.3.4 X11+GL if BigSur+ --wheel.c: first successful port w/homebrew tap patched mesa-25.3.4 (GL + X11!) ------- begin MacOS 26 Tahoe (release=25) compile ------------ --tksurfer.c: append hemi, selectedvtx to edit.dat on SEND PNT for xhemi corr --tksurfer.tcl: do sampclustctrls after "RD" readrawdata remind set sample point --lib/help/tksurfer/label_corr: update --[prev 3 a bit overkill :-} ] --tksurfer.tcl: MAKE FIRST VOL PAINTABLE reports buff mismatch (won't get here) --tksurfer.tcl: READ RAWDATA err on rawdata_tdim=1 (HEAD read sets rawdata_tdim) --tksurfer.c: rawdatasubbrik_to_paintable reports mismatched dims vs. just err --[prev blocks err: read 3D BRIK, calc covar, re-read 4D BRIK, make PAINTABLE --tksurfer.c: read_rawdata sets rawdataloaded=0 if rawdata_tdim from HEAD is 1 --tksurfer.tcl: fix forgot logcmd for rawdatasubbrik_to_paintable [tmprelease 260525] --tksurfer.tcl: add $isession to top of READ RAWDATA BRIK and REGISTRATION popup --lib/help/tksurfer/val3d_raw: update --tksurfer.tcl: calc covar catches non-zero sample points with normsearch OFF --tksurfer.tcl: rawdata: "MAKE FIRST VOL PAINTABLE" rawdatasubbrik_to_paintable --tksurfer.tcl: paint_real_or_complex OK if rawdataframerealloaded=1 --tksurfer.c: add/exp rawdataframerealloaded set by rawdatasubbrik_to_paintable --tksurfer.tcl: add "SHOW UNIQSAMP VERTICES" to "SELECT VERTEX BY NUMBER" popup --tksurfer.c: extra-safe NULL ptr check in rawdatasubbrik_to_paintable [tmprelease 260522] --tksurfer.c: fix left-out returns in show_timecourse error checks!! [tmprelease 260521] --lib/help/tksurfer/{label_corr,redraw}: update --ksurfer.tcl: add "SHOW VERTEX TIMECOURSE" button to select-by-vtx-num popup --tksurfer.c: functify show_timecourse and export to tcl --tksurfer.tcl: R-click "REDRAW" button: add "Select Vertex By Number Popup" --tksurfer.tcl: R-click "X" buttonbar: add "Read RawData Covariance" --tksurfer.c: add/export read_rawdata_covar (a bit overkill) --tksurfer.tcl: green selbgcol for hacked-in popup comboboxes (mkannot,rawdata) --tksurfer.c: rawdatasubbrik_to_paintable now accepts short read by read_rawdata --tksurfer.tcl: R-clk "X" buttonbar: add "Display Correlation for CurrVertex" --tksurfer.tcl: R-clk "X" buttonbar: add "{Calc,Write} RawData Covariance" --tksurfer.c: add/export copy_rowcovar_val() (find nearest uniqvtx to selected) --tksurfer.c: add/export calc_rawdata_covar(), write_rawdata_covar() --tksurfer.c: new tmp covar mat globals: covarmat{,_dim,_vnum,loaded} --tksurfer.c: sampvtxcnt, uniqvtxcnt made global for tcl report --tksurfer.tcl: R-clk menu "label:" line "X" refreshes corr over label/annot --tksurfer.tcl: NormSamp popup: enable/disable parms: normdfracflag, bokflag --tksurfer.tcl: UQ popup reports sampvtxcnt, uniqvtxcnt, vtxs per voxel --tksurfer.c: sampvtxcnt, uniqvtxcnt made global for tcl report ########################################################################## UPDATE: 02 May 2026 ########################################################################## ---------------- 02May2026 csurf0.8 tarballs ------------------------------ --lib/help/tksurfer/{save,goto}_pnt: update --tksurfer.c: sensibly rename: *vindex -> *selectedptr (in 2 functs below) --tksurfer.c: same fix as below for (script-only) select_talairach_point [tmprelease 260429] --tksurfer.c: funct below passed &selected(def=-1) so deref rngchk brks 1stcall --tksurfer.c: rm bad select_orig_vertex_coords err chk *vindex - broke 1st GOTO ...