cosmo meeg read layout skl

function layout = cosmo_meeg_read_layout(fn)
    % Read FieldTrip layout
    %
    % layout=cosmo_meeg_read_layout(fn)
    %
    % Inputs:
    %   fn                  Filename of layout file, or a string containing the
    %                       layout. In the latter case fn must contain at least
    %                       one newline ('\n') character.
    %                       A layout file is a text file with one line per
    %                       sensor, with each line containing the following
    %                       data separated by white-space:
    %                       1) sensor number (integer)
    %                       2) horizontal position (float)
    %                       3) vertical position (float)
    %                       4) width (float)
    %                       5) height (float)
    %                       6) label (string)
    %
    % Output:
    %   layout              struct with fields containing data for N sensors:
    %     .pos              Nx2 matrix with x and y position
    %     .width            Nx1 vector
    %     .height           Nx1 vector
    %     .label            Nx1 cell string with channel labels
    %
    % Notes:
    %   - whitespace is trimmed from the labels
    %   - the sensor number is not used; the order of the sensors in layout is
    %     the same as in the layout file
    %
    % #   For CoSMoMVPA's copyright information and license terms,   #
    % #   see the COPYING file distributed with CoSMoMVPA.           #

    check_layout_input(fn);

    if string_contains_newline(fn)
        lay_string = fn;
        fn_descr = @()sprintf('input:\n''%s''', lay_string);
    else
        lay_string = read_lay_string_from_file(fn);
        fn_descr = @()sprintf('file %s', fn);
    end

    layout = parse_layout(lay_string, fn_descr);

function lay_string = read_lay_string_from_file(fn)
    if ~exist(fn, 'file')
        error('layout file %s does ont exist', fn);
    end

    % read FT layout (.lay) file
    fid = fopen(fn);
    file_closer = onCleanup(@()fclose(fid));
    lay_string = fread(fid, inf, 'char=>char')';

function check_layout_input(fn)
    if ~ischar(fn)
        error('first argument must be string, found %s', class(fn));
    end

function tf = string_contains_newline(fn)
    tf = any(fn == sprintf('\n'));

function layout = parse_layout(lay_string, fn_descr)
    % pattern to match is integer, then 4 numeric values followed by a
    % string that can contain whitespaces and plus characters, followed by
    % newline
    integer = '(\d+)';
    float = '([\d\.-]+)';
    space = '\s+';
    channel_label = '([\w \t\r\f\v\+\-]+)';
    single_newline = '\n';

    pat = [integer, space, ...
           float, space, ...
           float, space, ...
           float, space, ...
           float, space, ...
           channel_label, single_newline];

    matches = regexp(sprintf('%s\n', lay_string), pat, 'tokens');
    if isempty(matches)
        error('No valid layout definition found in %s', fn_descr());
    end

    % convert to (nchannel x 6) matrix
    layout_matrix = cat(1, matches{:});

    % convert values in first five columns to numeric
    num_values_cell = layout_matrix(:, 1:5)';

    str_values = sprintf('%s %s %s %s %s; ', num_values_cell{:});
    num_values = str2num(str_values);

    % store layout information (omit channel number in first column)
    layout.pos    = num_values(:, 2:3);
    layout.width  = num_values(:, 4);
    layout.height = num_values(:, 5);

    % trim whitespace around channel names
    label = layout_matrix(:, 6);
    label = regexprep(label, '^\s*', '');
    label = regexprep(label, '\s*$', '');
    layout.label  = label;