function ft_plot_layout(layout, varargin)

% FT_PLOT_LAYOUT plots a two-dimensional channel layout
%
% Use as
%   ft_plot_layout(layout, ...)
% where the layout is a FieldTrip structure obtained from FT_PREPARE_LAYOUT.
%
% Additional options should be specified in key-value pairs and can be
%   'chanindx'    = logical vector or vector of indices with the channels to plot (default is all)
%   'point'       = 'yes' or 'no' (default 'yes'), plot markers for sensors, comment and scale
%   'box'         = 'yes' or 'no' (default 'yes'), plot boxes around the sensors, comment and scale
%   'label'       = 'yes' or 'no' (default 'yes'), plot the labels of the sensors, comment and scale
%   'labeloffset' = offset of label from point (default = 0)
%   'labelrotate' = scalar or vector with rotation angle (in degrees) per label (default = 0)
%   'labelalignh' = string or cell-array specifying the horizontal alignment of the text (default = 'center')
%   'labelalignv' = string or cell-array specifying the vertical alignment of the text (default = 'middle')
%   'mask'        = 'yes' or 'no' (default 'yes'), plot the interpolation area of the layout
%   'outline'     = 'yes' or 'no' (default 'yes'), plot the outline of the layout (e.g. head and MEG helmet)
%   'verbose'     = 'yes' or 'no' (default 'no'), print explanation of the figure to command window
%   'fontcolor'   = string, text color specification (default = 'k')
%   'fontsize'    = scalar, sets the size of the text (default = 10)
%   'fontunits'   = string, units of the font size (default is the Matlab's session default)
%   'fontname'    = string, font name (default is the Matlab's session default)
%   'fontweight'  = scalar, sets the size of the text (default = 10)
%   'interpreter' = string, 'none', 'tex' or 'latex' (default = 'tex')
%
% The following options control the markers of the sensors. If any is defined, the other two must be defined as well.
% Further note that if 'chanindx' is used, the number of elements in each choice should correspond to the original
% labels in the layout, and not to the chosen subset.
%   'pointsymbol' = string with symbol (e.g. 'o' or 'oooxxx')
%   'pointcolor'  = string with color (e.g. 'k'), or an NX3 matrix of RGB values
%   'pointsize'   = scalar or vector for marker size
% The default marker is a blue dot sorrunded by a yellow circle.
%
% It is possible to plot the object in a local pseudo-axis (c.f. subplot), which is specified as follows
%   'hpos'        = horizontal position of the lower left corner of the local axes
%   'vpos'        = vertical position of the lower left corner of the local axes
%   'width'       = width of the local axes
%   'height'      = height of the local axes
%
% See also FT_PREPARE_LAYOUT, FT_PLOT_TOPO

% Copyright (C) 2009-2026, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% get the optional input arguments
chanindx     = ft_getopt(varargin, 'chanindx',     []);
hpos         = ft_getopt(varargin, 'hpos',         0);
vpos         = ft_getopt(varargin, 'vpos',         0);
width        = ft_getopt(varargin, 'width',        []);
height       = ft_getopt(varargin, 'height',       []);
box          = ft_getopt(varargin, 'box',          true);
label        = ft_getopt(varargin, 'label',        true);
point        = ft_getopt(varargin, 'point',        ~istrue(label));
labeloffset  = ft_getopt(varargin, 'labeloffset',  0);
labelxoffset = ft_getopt(varargin, 'labelxoffset', labeloffset);
labelyoffset = ft_getopt(varargin, 'labelyoffset', labeloffset*1.5);
mask         = ft_getopt(varargin, 'mask',         true);
outline      = ft_getopt(varargin, 'outline',      true);
verbose      = ft_getopt(varargin, 'verbose',      false);
pointsymbol  = ft_getopt(varargin, 'pointsymbol',  '.');
pointcolor   = ft_getopt(varargin, 'pointcolor',   'b');
pointsize    = ft_getopt(varargin, 'pointsize',    6);

% these have to do with the font
fontcolor   = ft_getopt(varargin, 'fontcolor', 'k'); % default is black
fontsize    = ft_getopt(varargin, 'fontsize',   get(0, 'defaulttextfontsize'));
fontname    = ft_getopt(varargin, 'fontname',   get(0, 'defaulttextfontname'));
fontweight  = ft_getopt(varargin, 'fontweight', get(0, 'defaulttextfontweight'));
fontunits   = ft_getopt(varargin, 'fontunits',  get(0, 'defaulttextfontunits'));
% these have to do with the font
interpreter  = ft_getopt(varargin, 'interpreter', 'none'); % none, tex or latex

% some stuff related to some refined label plotting
labelrotate   = ft_getopt(varargin, 'labelrotate',  0);
labelalignh   = ft_getopt(varargin, 'labelalignh',  'center');
labelalignv   = ft_getopt(varargin, 'labelalignv',  'middle');

% convert between true/false/yes/no etc. statements
point   = istrue(point);
box     = istrue(box);
label   = istrue(label);
mask    = istrue(mask);
outline = istrue(outline);
verbose = istrue(verbose);

if ischar(pointcolor), pointcolor = colorspec2rgb(pointcolor); end

if ~(point || box || label || mask || outline)
  % there is nothing to be plotted
  return;
end

% everything is added to the current figure
holdflag = ishold;
if ~holdflag
  hold on
end

% make a selection of the channels
if ~isempty(chanindx)
  layout.pos    = layout.pos(chanindx,:);
  layout.width  = layout.width(chanindx);
  layout.height = layout.height(chanindx);
  layout.label  = layout.label(chanindx);
end

% the units can be arbitrary (e.g. relative or pixels), so we need to compute the right scaling factor and offset
% create a matrix with all coordinates from positions, mask, and outline
allCoords = layout.pos;
if isfield(layout, 'mask') && ~isempty(layout.mask)
  for k = 1:numel(layout.mask)
    allCoords = [allCoords; layout.mask{k}];
  end
end
if isfield(layout, 'outline') &&~isempty(layout.outline)
  for k = 1:numel(layout.outline)
    allCoords = [allCoords; layout.outline{k}];
  end
end

naturalWidth = (max(allCoords(:,1))-min(allCoords(:,1)));
naturalHeight = (max(allCoords(:,2))-min(allCoords(:,2)));

if isempty(width) && isempty(height)
  xScaling = 1;
  yScaling = 1;
elseif isempty(width) && ~isempty(height)
  % height specified, auto-compute width while maintaining aspect ratio
  yScaling = height/naturalHeight;
  xScaling = yScaling;
elseif ~isempty(width) && isempty(height)
  % width specified, auto-compute height while maintaining aspect ratio
  xScaling = width/naturalWidth;
  yScaling = xScaling;
else
  % both width and height specified
  xScaling = width/naturalWidth;
  yScaling = height/naturalHeight;
end

X      = layout.pos(:,1)*xScaling + hpos;
Y      = layout.pos(:,2)*yScaling + vpos;
Width  = layout.width*xScaling;
Height = layout.height*yScaling;
Lbl    = layout.label;

if point
  % check whether a for loop is required, otherwise the points can be printed in a single shot
  if size(pointcolor, 1)==1 && isscalar(pointsymbol) && isscalar(pointsize)
    plot(X, Y, 'marker', pointsymbol, 'markerfacecolor', pointcolor, 'markersize', pointsize, 'linestyle','none');
  else
    if size(pointcolor, 1)==1
      pointcolor = repmat(pointcolor, numel(X), 1);
    end
    if isscalar(pointsymbol)
      pointsymbol = repmat(pointsymbol, numel(X), 1);
    end
    if isscalar(pointsize)
      pointsize = repmat(pointsize, numel(X), 1);
    end
    for k = 1:numel(X)
      plot(X(k), Y(k), 'marker', pointsymbol(k), 'markerfacecolor', pointcolor(k, :), 'markersize', pointsize(k), 'linestyle','none');
    end % for all points
  end % if for loop needed
end % if point

if label
  % the MATLAB text function fails if the position for the string is specified in single precision
  X = double(X);
  Y = double(Y);

  % check whether a for loop is required, otherwise the labels can be printed in a single shot
  if isscalar(labelrotate) && isscalar(labelxoffset) && isscalar(labelyoffset) && ischar(labelalignh) && ischar(labelalignv)
    text(X+labelxoffset, Y+labelyoffset, Lbl, 'horizontalalignment', labelalignh, 'verticalalignment', labelalignv, 'rotation', labelrotate, 'interpreter', interpreter, 'color', fontcolor, 'fontunits', fontunits, 'fontsize', fontsize, 'fontname', fontname, 'fontweight', fontweight);
  else
    if isscalar(labelrotate)
      labelrotate = repmat(labelrotate, size(Lbl));
    end
    if isscalar(labelxoffset)
      labelxoffset = repmat(labelxoffset, size(Lbl));
    end
    if isscalar(labelyoffset)
      labelyoffset = repmat(labelyoffset, size(Lbl));
    end
    if ischar(labelalignh)
      labelalignh = repmat({labelalignh}, size(Lbl));
    end
    if ischar(labelalignv)
      labelalignv = repmat({labelalignv}, size(Lbl));
    end
    for k=1:numel(Lbl)
      text(X(k)+labelxoffset(k), Y(k)+labelyoffset(k), Lbl{k}, 'horizontalalignment', labelalignh{k}, 'verticalalignment', labelalignv{k}, 'rotation', labelrotate(k), 'interpreter', interpreter, 'color', fontcolor, 'fontunits', fontunits, 'fontsize', fontsize, 'fontname', fontname, 'fontweight', fontweight);
    end % for all labels
  end % if for loop needed
end % if label

if box
  line([X-Width/2 X+Width/2 X+Width/2 X-Width/2 X-Width/2]',[Y-Height/2 Y-Height/2 Y+Height/2 Y+Height/2 Y-Height/2]', 'color', [0 0 0]);
end

if outline && isfield(layout, 'outline')
  if verbose
    fprintf('solid lines indicate the outline, e.g. head shape or sulci\n');
  end
  for i=1:length(layout.outline)
    if ~isempty(layout.outline{i})
      X = layout.outline{i}(:,1)*xScaling + hpos;
      Y = layout.outline{i}(:,2)*yScaling + vpos;
      h = line(X, Y);
      set(h, 'color', 'k');
      set(h, 'linewidth', 2);
    end
  end
end

if mask && isfield(layout, 'mask')
  if verbose
    fprintf('dashed lines indicate the mask for topograpic interpolation\n');
  end
  for i=1:length(layout.mask)
    if ~isempty(layout.mask{i})
      X = layout.mask{i}(:,1)*xScaling + hpos;
      Y = layout.mask{i}(:,2)*yScaling + vpos;
      % the polygon representing the mask should be closed
      X(end+1) = X(1);
      Y(end+1) = Y(1);
      h = line(X, Y);
      set(h, 'color', 'k');
      set(h, 'linewidth', 1.5);
      set(h, 'linestyle', ':');
    end
  end
end

if isempty(width) && isempty(height)
  axis auto
  axis equal
  axis off
end

if ~holdflag
  hold off
end
