Code covered by the BSD License  

Highlights from
Matfig2PGF

from Matfig2PGF by Paul Wagenaars
Convert figures to PGF files that can be included in LaTeX documents.

matfig2pgf( varargin )
function matfig2pgf( varargin )
%MATFIG2PGF  Convert figures to PGF for inclusion in LaTeX documents.
%
%   Matfig2PGF converts a figure to the Portable Graphics Format (PGF).
%   The PGF file can be included in a LaTeX document.
%
%   matfig2pgf(filename)
%   matfig2pgf('option1', option_value1, 'option2', option_value2, ...)
%
%   Example:
%     matfig2pgf('filename', 'figure.pgf', 'fignr', 2, 'figwidth', 5)
%
%   filename - filename where the figure will be stored. If no extension is
%              provided, the .pgf is appended.
%
%   Accepted options:
%     'fignr'
%       uint - number of the figure to convert to pgf. If fignr = 0 then
%       the current figure is used (the gcf command) (default: 0)
%
%     'filename'
%       string - filename where the figure will be stored. If no extension is
%       provided, the .pgf is appended. If no filename is specified a
%       dialog box appears that allows the user to select a file.
%
%     'figwidth'
%       float - width of the figure in cm. The figure height will
%       automatically be calculated, based on the aspect ratio of the
%       figure. If this value is set to zero or less, the figure is not
%       resized. (default: 10)
%
%     'fontname'
%       string - font to be used for all texts in the figure. All text in
%       the figure will be set to this font. Note that this does not
%       specify the actual font used when included in a LaTeX document. In
%       order to get the best result, the font specified here should be as
%       close as possible to the font that will be used in the LaTeX
%       document. (default: 'Times')
%
%     'fontsize'
%       float - size of the fonts for all texts. All text in the figure
%       will be set to this size. This size should be the same as used in
%       the LaTeX document. If this value is zero or less the fonts are not
%       resized. (default: 10)
%
%     'texticklabelsize'
%       string - font size for tick labels in LaTeX output. This value is not
%       used for resizing the figure, it's only used in the LaTeX output.
%       (default: 'normalsize')
%
%     'textextlabelsize'
%       string - font size for all other text labels in LaTeX output. This value
%       is not used for resizing the figure, it's only used in the LaTeX output.
%       (default: 'normalsize')
%
%     'drawfigoutline'
%       boolean - draw a box around the figure (default: false)
%
%     'minlinewidth'
%       float - minimum linewidth in pt. Lines will never be thinner than 
%       this width (default: 0.5pt)
%
%     'includedraftcode'
%       boolean - include a few lines of code in the PGF file. If these
%       lines are added and the pgf package is included using the 'draft'
%       option, then only the axes and labels are drawn. This reduces the
%       compilation time for graphs with a large number of data points.
%       (default: true)
%
%     'textprecode'
%       string - LaTeX code that will be inserted before all text labels in
%       the PGF file. (default: '')
%
%     'textpostcode'
%       string - LaTeX code that will be inserted after all text labels in
%       the PGF file. (default: '')
%
%     'mathprecode'
%       string - LaTeX code that will be inserted before all math code.
%       (default: '$')
%
%     'mathpostcode'
%       string - LaTeX code that will be inserted after all math code.
%      (default: '$')
%
%     'reducedlineerror'
%       float - in order to reduce the size of the PGF file Matfig2PGF will
%       reduce the number of data points, as long as it does not affect the
%       resulting image. If the distance between a data point and the
%       straight line between the previous and next data point that are
%       drawn is less than this value, the data point is left out. This
%       distance is expressed in terms of the line width. A value of 0.1
%       means that the error can not be more than 10% of the line width.
%       (default: 0.1)
%
%     'converttexttolatex'
%       boolean - if set to true Matfig2PGF will try to convert all text
%       strings to proper LaTeX code. E.g. the original string 'x_1' will
%       be converted to '$x_1$'. It is possible to specify your own text
%       that should be used in the PGF file. This can be done by setting
%       the 'UserData' field of the text object. E.g. by
%       set(h, 'UserData', 'matfig2pgf_text: <latex code>');
%       (default: true)
%
%     'snapdistance'
%       float - the position and size of the axes can change, depending on
%       the length of the labels. Especially, the y-axis changes with the
%       ylabel. Compare for example the y-axis position when ylim([0 9])
%       with ylim([0 0.01]). In a report it looks bette when all axes have
%       the same size. Therefore, Matfig2PGF will snap the axes border to
%       values specified in the 'snapverticalborders' and 
%       'snaphorizontalborders' options if they are within the 'snapdistance'.
%       This distance is specified as a normalized value. A value between
%       0 and 1, where (0,0) corresponds to the bottom-left corner of the
%       figure and (1,1) the top-right corner. Note that this can cause the
%       figure to become slightly larger than determined by the 'figwidth'
%       option. (default: 0.1)
%
%     'snapleft'
%       float - snap the top border of axes objects to this distance from
%       the left border of the figure, if they are within 'snapdistance'. This
%       value is relative to font size. The font size, specified in pt, is
%       converted to a normalized value (relative to the figure size). E.g.
%       if the font size = 10pt, figure width = 10cm and snapleft = 5,
%       the normalized snapleft is:
%         10[pt] * 1/72[in/pt] * 2.54[cm/in] / 10[cm] * 5 = 0.1764
%       If set to NaN 'snapleft' is ignored (default: 4)
%
%     'snapright'
%       float - snap the right border of axes objects to this distance from
%       the right border of the figure, if they are within 'snapdistance'. See
%       'snapleft' and 'snapdistance' for more information. If set to NaN
%       'snapright' is ignored (default: 0.8)
%
%     'snapbottom'
%       float - snap the bottom border of axes objects to this distance from
%       the bottom of the figure, if they are within 'snapdistance'. See
%       'snapleft' and 'snapdistance' for more information. If set to NaN
%       'snapbottom is ignored. (default: 3)
%
%     'snaptop'
%       float - snap the top border of axes objects to this distance from
%       the top of the figure, if they are within 'snapdistance'. See
%       'snapleft' and 'snapdistance' for more information. If the axis 
%       object has a title, 'snaptop' is automatically increased by 1. If
%       set to NaN 'snaptop' is ignored. (default: 0.8)
%
%     'noresize'
%       boolean - if set to true the figure is not resized. (default:
%       false)
%
%     'nosave'
%       boolean - if set to true the figure is not saved to file.
%       (default: false)
%
% It is possible to specify your own default options in useroptions.cfg

clear global matfig2pgf_opt;
clear global matfig2pgf_props;
clear global matfig2pgf_version;

global matfig2pgf_version;
matfig2pgf_version = 'Matfig2PGF v0.3.9';

global matfig2pgf_opt;
matfig2pgf_options('set_global', varargin);

fprintf('%s\n', matfig2pgf_version);

% Get the figure handle
if matfig2pgf_opt.fignr > 0
    matfig2pgf_opt.fighandle = matfig2pgf_opt.fignr;
else
    matfig2pgf_opt.fighandle = gcf;
end
fprintf('Figure number: %d\n', matfig2pgf_opt.fighandle);

% Resize the figure
if ~matfig2pgf_opt.noresize
    resize_figure();
end

% Save the figure as pgf to file
if ~matfig2pgf_opt.nosave
    save_to_file();
end

% Try to restore the figure to it's original state
if ~matfig2pgf_opt.keepresizedfigure && ~matfig2pgf_opt.noresize ...
        && ~matfig2pgf_opt.nosave
    restore_remembered_properties
end

clear global matfig2pgf_opt;
clear global matfig2pgf_props;
clear global matfig2pgf_version;

%--------------------------------------------------------------------------



%% FUNCTION RESIZE_FIGURE
%
%  Resize the figure and fonts to match the desired size.
%
%  resize_figure()
%
%--------------------------------------------------------------------------
function resize_figure()
global matfig2pgf_opt;

% Resize figure to correct size
set_and_remember(matfig2pgf_opt.fighandle, 'Units', 'centimeters');
position = get(matfig2pgf_opt.fighandle, 'Position');
if matfig2pgf_opt.figwidth > 0
    matfig2pgf_opt.figheight = matfig2pgf_opt.figwidth * position(4) / position(3);
    fprintf('Figure size: %gcm x %gcm\n', matfig2pgf_opt.figwidth, ...
        matfig2pgf_opt.figheight);
    position(3:4) = [matfig2pgf_opt.figwidth matfig2pgf_opt.figheight];
    set_and_remember(matfig2pgf_opt.fighandle, 'Position', position);
else
    matfig2pgf_opt.figwidth = position(2);
    matfig2pgf_opt.figheight = position(4);
end

% Change the size and name of all fonts, so that they match the LaTeX
% result
if matfig2pgf_opt.fontsize > 0
    fprintf('Font size: %gpt\n', matfig2pgf_opt.fontsize);
    change_fonts(matfig2pgf_opt.fighandle);
end

% Snap the borders of the axes objects, if necessary
if (matfig2pgf_opt.snapdistance > 0)
    snap_axes_borders(matfig2pgf_opt.fighandle);
end

% A small pause makes sure that MATLAB is finished resizing and moving all
% objects
pause(0.01);

%- end of function resize_figure ------------------------------------------



%% FUNCTION SAVE_TO_FILE
%
%  Save the figure as pgf to a file.
%
%  save_to_file()
%
%--------------------------------------------------------------------------
function save_to_file()
global matfig2pgf_opt;
global matfig2pgf_version;

% If no filename is specified, ask the user for one.
if length(matfig2pgf_opt.filename) <= 0
     [file, path] = uiputfile('*.pgf', 'Save figure as...');
     if file == 0
         error('matfig2pgf:save_to_file:CancelledByUser', 'Cancelled by user');
     end
     matfig2pgf_opt.filename = fullfile(path, file);
end
% Check whether the filename has an extension
[pathstr, name, ext] = fileparts(matfig2pgf_opt.filename);
if isempty(ext)
    ext = '.pgf';
end
matfig2pgf_opt.filename = fullfile(pathstr, [name ext]);
fprintf('Saving to: %s\n', matfig2pgf_opt.filename);

% Get the figures dimensions
set_and_remember(matfig2pgf_opt.fighandle, 'Units', 'centimeters');
position = get(matfig2pgf_opt.fighandle, 'Position');
matfig2pgf_opt.figwidth = position(3);
matfig2pgf_opt.figheight = position(4);


fp = fopen(matfig2pgf_opt.filename,'w');
if fp == -1
    error('matfig2pgf:CannotOpenFile','Unable to open %s for writing', matfig2pgf_opt.filename);
end
fprintf(fp, '%% This file has been created by %s\n', matfig2pgf_version);
fprintf(fp, '\\begin{pgfpicture}\n');

% Draw the edge of the figure
if matfig2pgf_opt.drawfigoutline
    fprintf(fp, '  %% Figure outline\n');
    fprintf(fp,'  \\pgfpathrectangle{\\pgfpointorigin}{\\pgfpoint{%gcm}{%gcm}}\n',...
        matfig2pgf_opt.figwidth, matfig2pgf_opt.figheight);
    fprintf(fp,'  \\pgfusepath{stroke}\n');
end

% Draw all children
set_and_remember(0, 'ShowHiddenHandles', 'on');
handle_all_children(matfig2pgf_opt.fighandle, fp);

if matfig2pgf_opt.includedraftcode
    fprintf(fp,'  \\makeatletter\\ifpgf@draftmode\\makeatother');
    fprintf(fp,'\\pgftext[x=%gcm,y=%gcm]{%s\\Huge{DRAFT}%s}\\fi\n', ...
        0.5*matfig2pgf_opt.figwidth, 0.5*matfig2pgf_opt.figheight, ...
        matfig2pgf_opt.textprecode, matfig2pgf_opt.textpostcode);
end
fprintf(fp, '\\end{pgfpicture}\n');
fclose(fp);

%- end of function save_to_file -------------------------------------------



%% FUNCTION HANDLE_ALL_CHILDREN
%
%  Draw all children of a graphics object (if they need to be drawn).
%
%  handle_all_children( handle, fp )
%
%    handle - handle to grsphics object
%    fp     - file pointer to the file were the pgf code is written to
%
%--------------------------------------------------------------------------
function handle_all_children( handle, fp )

child_handles = get(handle, 'Children');
for i = length(child_handles):-1:1
    child_handle = child_handles(i);
    switch get(child_handle, 'Type')
        case 'axes'
            draw_axes(child_handle, fp);
        case 'line'
            draw_line(child_handle, fp);
        case 'text'
            draw_text(child_handle, fp);
        case 'patch'
            draw_patch(child_handle, fp);
        case 'hggroup'
        case 'hgtransform'
        case 'uitoolbar'
        case 'uimenu'
        case 'uicontextmenu'
        case 'uitoggletool'
        case 'uitogglesplittool'
        case 'uipushtool'
        case 'hgjavacomponent'
        otherwise
            fprintf('I don''t know how to handle this object: %s\n', ...
                get(child_handle, 'Type'));
    end
    
    % Also draw all children
    handle_all_children(child_handle, fp);
end

%- end of function handle_all_children ------------------------------------



%% FUNCTION DRAW_PATCH
%
%  Draws a 'patch' graphic object
%
%  draw_patch(handle, fp)
%
%    handle - handle to the patch object
%    fp     - file pointer to were the pgf code must be written
%--------------------------------------------------------------------------
function draw_patch(handle, fp)

global matfig2pgf_opt;

if ~strcmp(get(handle, 'Visible'), 'on')
    return
end

linewidth = get(handle, 'LineWidth');
edgecolor = get(handle, 'EdgeColor');
linestyle = get(handle, 'LineStyle');
facecolor = get(handle, 'FaceColor');
cdata = get(handle, 'CData');

xdata = get(handle, 'XData');
ydata = get(handle, 'YData');

fprintf(fp, '  %% Patch object\n');
fprintf(fp, '  \\begin{pgfscope}\n');
if strcmp(edgecolor, 'flat')
    if size(cdata,3) > 1
        edgecolor = cdata(1,1,:);
    else % indexed colors
        colormap = get(matfig2pgf_opt.fighandle, 'ColorMap');
        if strcmp(get(handle, 'CDataMapping'), 'scaled')
            % need to scale within clim
            clim = get(matfig2pgf_opt.CurrentAxesHandle, 'clim');
            colorindex = round( interp1(clim, ...
				   [1 length(colormap)], cdata(1, 1)) );
        else
            % direct index
            colorindex = cdata(1, 1);
        end;
        edgecolor = colormap(colorindex,:);
    end
end
generate_linestyle_code(fp, linestyle, edgecolor, linewidth, 4);

% Loop through all columns in XData (and YData)
for col = 1:size(xdata,2)
    xydata = transpose([xdata(:,col) ydata(:,col)]);
    % If the parent object is an axes the coordinate unit is 'data',
    % otherwise it is 'normalized'
    parentAxes = get_parent_axes_handle(handle);
    if parentAxes >= 0
        xydata = data2norm(parentAxes, xydata);
    end
    xydata = norm2abs(xydata);

    if strcmp(facecolor, 'flat')
        colormap = get(matfig2pgf_opt.fighandle, 'ColorMap');
        if strcmp(get(handle, 'CDataMapping'), 'scaled')
            % need to scale within clim
            % see MATLAB's manual page for caxis for details
    	    clim = get(matfig2pgf_opt.CurrentAxesHandle, 'clim');
            m = size(colormap,1);
            if (cdata<=clim(1))
        		colorindex = 1;
            elseif (cdata>=clim(2))
        		colorindex = m;
            else
            	colorindex = fix((cdata-clim(1))/(clim(2)-clim(1))*m)+1;
            end
        else
            % direct index
    	    colorindex = cdata(1, 1);
        end
        facecolor = colormap(colorindex,:);
    elseif ( isreal(facecolor) && length(facecolor)==3 )
    else
        error( 'I don''t know how to handle the FaceColor value of %s .', facecolor );
    end
    fprintf(fp, '    \\definecolor{matfig2pgf_facecolor}{rgb}{%g,%g,%g}',...
	facecolor(1), facecolor(2), facecolor(3));
    fprintf(fp, '\\pgfsetfillcolor{matfig2pgf_facecolor}\n');
    
    % Draw the line and fill
    fprintf(fp, '    \\pgfplothandlerlineto\n');
    generate_plotstream_code(fp, xydata, 4);
    fprintf(fp, '    \\pgfpathclose\n');
    if ~strcmp(linestyle, 'none') && ~strcmp(facecolor, 'none')
        fprintf(fp, '    \\pgfusepath{stroke,fill}\n');
    elseif ~strcmp(linestyle, 'none')
        fprintf(fp, '    \\pgfusepath{stroke}\n');
    elseif ~strcmp(facecolor, 'none')
        fprintf(fp, '    \\pgfusepath{fill}\n');
    end
end
fprintf(fp, '  \\end{pgfscope}\n');

% Markers ***TODO***


%- end of function draw_patch ---------------------------------------------


%% FUNCTION DRAW_AXES
%--------------------------------------------------------------------------
function draw_axes(handle, fp)

if ~strcmp(get(handle, 'Visible'), 'on')
    return
end

global matfig2pgf_opt;

% Calculate coordinates of lower-left and upper-right corners of the entire
% axes
position = get(handle, 'Position');
bgcolor = get(handle, 'Color');
x1 = position(1);
x2 = x1+position(3);
y1 = position(2);
y2 = y1+position(4);
coor = norm2abs([x1 x2 ; y1 y2]);
widthheight = norm2abs(transpose(position(3:4)));

fprintf(fp, '  %% Axes\n');
if ~strcmpi(bgcolor, 'none')
    fprintf(fp, '  \\begin{pgfscope}\n');
    fprintf(fp, '    \\definecolor{matfig2pgf_color}{rgb}{%g,%g,%g}', ...
        bgcolor(1), bgcolor(2), bgcolor(3));
    fprintf(fp, '\\pgfsetfillcolor{matfig2pgf_color}\n');
    fprintf(fp, '    \\pgfpathrectangle{\\pgfpoint{%gcm}{%gcm}}{\\pgfpoint{%gcm}{%gcm}}\n',...
        coor(1),coor(2),widthheight(1),widthheight(2));
    fprintf(fp, '    \\pgfusepath{fill}\n');
    fprintf(fp, '  \\end{pgfscope}\n');
end

draw_grid(handle, fp);
draw_tick_marks(handle, fp);

% Draw outer line of the axes
linewidth = max(get(handle, 'LineWidth'), matfig2pgf_opt.minlinewidth);
fprintf(fp,'  %% Outer axes line\n');
box = get(handle, 'Box');
xcolor = get(handle, 'XColor');
ycolor = get(handle, 'YColor');
if strcmp(box, 'on')
    fprintf(fp,'  \\begin{pgfscope}\n');
    fprintf(fp,'    \\pgfsetlinewidth{%.1fpt}\n', linewidth);
    fprintf(fp,'    \\pgfpathrectangle{\\pgfpoint{%gcm}{%gcm}}{\\pgfpoint{%gcm}{%gcm}}\n',...
        coor(1,1),coor(2,1),widthheight(1),widthheight(2));
    fprintf(fp, '    \\pgfusepath{stroke}\n');
    fprintf(fp, '  \\end{pgfscope}\n');
end
if ~strcmp(box, 'on') || any(xcolor ~= [0 0 0])
    % The axes box is not drawn, therefore we need to draw lines for the X-
    % and Y-axis.
    fprintf(fp,'  \\begin{pgfscope}\n');
    fprintf(fp,'    \\pgfsetlinewidth{%.1fpt}\n', linewidth);
    specify_pgf_color(fp, '    ', 'line', xcolor);
    if strcmp(get(handle, 'XAxisLocation'), 'bottom')
         fprintf(fp, '    \\pgfpathmoveto{\\pgfpoint{%fcm}{%fcm}}', ...
             coor(1,1), coor(2,1));
         fprintf(fp, '\\pgfpathlineto{\\pgfpoint{%fcm}{%fcm}}\n', ...
             coor(1,2), coor(2,1));
    else
         fprintf(fp, '    \\pgfpathmoveto{\\pgfpoint{%fcm}{%fcm}}', ...
             coor(1,1), coor(2,2));
         fprintf(fp, '\\pgfpathlineto{\\pgfpoint{%fcm}{%fcm}}\n', ...
             coor(1,2), coor(2,2));         
    end
    fprintf(fp, '    \\pgfusepath{stroke}\n');
    fprintf(fp, '  \\end{pgfscope}\n');
end
if ~strcmp(box, 'on') || any(ycolor ~= [0 0 0])    
    fprintf(fp,'  \\begin{pgfscope}\n');
    fprintf(fp,'    \\pgfsetlinewidth{%.1fpt}\n', linewidth);
    specify_pgf_color(fp, '    ', 'line', ycolor);
    if strcmp(get(handle, 'YAxisLocation'), 'left')
         fprintf(fp, '    \\pgfpathmoveto{\\pgfpoint{%fcm}{%fcm}}', ...
             coor(1,1), coor(2,1));
         fprintf(fp, '\\pgfpathlineto{\\pgfpoint{%fcm}{%fcm}}\n', ...
             coor(1,1), coor(2,2));
    else
         fprintf(fp, '    \\pgfpathmoveto{\\pgfpoint{%fcm}{%fcm}}', ...
             coor(1,2), coor(2,1));
         fprintf(fp, '\\pgfpathlineto{\\pgfpoint{%fcm}{%fcm}}\n', ...
             coor(1,2), coor(2,2));         
    end
    fprintf(fp, '    \\pgfusepath{stroke}\n');
    fprintf(fp, '  \\end{pgfscope}\n');
end

draw_tick_labels(handle, fp);

matfig2pgf_opt.CurrentAxesHandle = handle;

%-- end of draw_axes function ---------------------------------------------



%% FUNCTION DRAW_GRID
%
%    Draw the grid, if the XGrid and/or YGrid property are set 'on'
% 
%    draw_grid( handle, fp )
% 
%--------------------------------------------------------------------------
function draw_grid( handle, fp )

global matfig2pgf_opt;

xgrid = get(handle, 'XGrid');
ygrid = get(handle, 'YGrid');
if strcmp(xgrid,'off') && strcmp(ygrid,'off')
    return
end

position = get(handle, 'Position');
x1 = position(1);
x2 = x1+position(3);
y1 = position(2);
y2 = y1+position(4);
coor = norm2abs([x1 x2 ; y1 y2]);
drawBox = strcmp(get(handle, 'Box'), 'on');
linewidth = max(0.67*get(handle, 'LineWidth'), matfig2pgf_opt.minlinewidth);

fprintf(fp, '  %% Grid\n');
fprintf(fp, '  \\begin{pgfscope}\n');
fprintf(fp, '    \\pgfsetlinewidth{%.1fpt}\n', linewidth);
fprintf(fp, '    \\pgfsetdash{{\\pgflinewidth}{0.5mm}}{0pt}\n');

% Draw the vertical grid lines
if strcmp(xgrid,'on')
    xtick = get_normalized_xtick(handle, drawBox, true);
    if ~isempty(xtick)
        fprintf(fp, '    \\foreach \\x in {');
        for i = 1:length(xtick)
            tempcoor = norm2abs([xtick(i) 0]);
            if i > 1
                fprintf(fp, ',');
            end
            fprintf(fp, '%g', tempcoor(1));
        end
        fprintf(fp, '}\n    {\n');
        fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{\\x cm}{%gcm}}\\pgfpathlineto{\\pgfpoint{\\x cm}{%gcm}}\n',...
            coor(2,1), coor(2,2));
        fprintf(fp, '    }\n');
    end
end

% Draw the horizontal grid lines
if strcmp(ygrid,'on')
    ytick = get_normalized_ytick(handle, drawBox, true);
    if ~isempty(ytick)
        fprintf(fp, '    \\foreach \\y in {');
        for i = 1:length(ytick)
            tempcoor = norm2abs([0 ytick(i)]);
            if i > 1
                fprintf(fp, ',');
            end
            fprintf(fp, '%g', tempcoor(2));
        end
        fprintf(fp, '}\n    {\n');
        fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{%gcm}{\\y cm}}\\pgfpathlineto{\\pgfpoint{%gcm}{\\y cm}}\n',...
            coor(1,1), coor(1,2));
        fprintf(fp, '    }\n');
    end
end

fprintf(fp,'    \\pgfusepath{stroke}\n');
fprintf(fp,'  \\end{pgfscope}\n');

%-- end of function draw_grid ---------------------------------------------



%% FUNCTION DRAW_TICK_LABELS
%
%    Draw the tick labels (the numbers near the tick marks)
% 
%    draw_tick_labels( handle, fp )
% 
%--------------------------------------------------------------------------
function draw_tick_labels( handle, fp )

global matfig2pgf_opt;

% Calculate coordinates of lower-left and upper-right corners of the entire
% axes
position = get(handle, 'Position');
x1 = position(1);
x2 = x1 + position(3);
y1 = position(2);
y2 = y1 + position(4);

fprintf(fp, '  %% Tick labels\n');
texticklabelsize = matfig2pgf_opt.texticklabelsize;
if (~isempty(texticklabelsize)) && (~strcmp(texticklabelsize, 'normalsize'))
    fprintf(fp, ['  {\\' texticklabelsize '\n']);
end

% Draw tick labels at x-axis
xticklabels = get(handle, 'XTickLabel');
xtick = get_normalized_xtick(handle);
xtick_data = get(handle, 'XTick');
xloc = get(handle, 'XAxisLocation');
xcolor = get(handle, 'XColor');
isLogScale = strcmp(get(handle, 'XScale'), 'log') && strcmpi(get(handle,'XTickLabelMode'), 'auto');
if strcmpi(xloc, 'top')
    labely = y2;
    textanchor = 'bottom';
    coor2offset = 0.1;
    loganchor = 'bottom';
else
    labely = y1;
    textanchor = 'top';
    coor2offset = -0.1;
    loganchor = 'top';
end
if ~isempty(xticklabels)
    fprintf(fp, '  \\begin{pgfscope}\n');
    specify_pgf_color(fp, '    ', 'fill', xcolor);
    
	multiplier = [];
	for i = 1:length(xtick)
        x = xtick(i);
        coor = norm2abs([x, labely]);
        label = trim_whitespace(xticklabels(1+mod(i-1,length(xticklabels)),:));
        if iscell(label)
            label = cell2mat(label);
        end
        if ~isLogScale
            label_num = str2double(label);
            if ~isempty(label_num) && (label_num ~= 0)
                multiplier = [multiplier xtick_data(i)/label_num];
            end
            fprintf(fp,'    \\pgftext[x=%gcm,y=%gcm,%s]{%s%s%s}\n', ...
                coor(1), coor(2)+coor2offset, textanchor, matfig2pgf_opt.mathprecode, ...
                label, matfig2pgf_opt.mathpostcode);
        else
            fprintf(fp,'    \\pgftext[x=%gcm,y=%gcm,%s]{%s10^{%s}%s}\n', ...
                coor(1), coor(2)+coor2offset, textanchor, matfig2pgf_opt.mathprecode, ...
                label, matfig2pgf_opt.mathpostcode);
        end
	end
	if ~isLogScale && (abs(mean(multiplier)-1) > 1e-10) && strcmpi(get(handle,'XTickLabelMode'), 'auto')
        x = position(1) + position(3);
        y = position(2);
        coor = norm2abs([x y]);
        fprintf(fp,'    \\pgftext[x=%gcm,y=%gcm,left,%s]{%s\\times 10^{%g}%s}\n', ...
            coor(1)+0.2, coor(2), loganchor, matfig2pgf_opt.mathprecode, ...
            log10(mean(multiplier)), matfig2pgf_opt.mathpostcode);
    end
    fprintf(fp, '  \\end{pgfscope}\n');
end

% Draw tick labels at y-axis
yticklabels = get(handle, 'YTickLabel');
ytick = get_normalized_ytick(handle);
ytick_data = get(handle, 'YTick');
yloc = get(handle, 'YAxisLocation');
ycolor = get(handle, 'YColor');
isLogScale = strcmp(get(handle, 'YScale'), 'log') && strcmpi(get(handle,'YTickLabelMode'), 'auto');
if strcmpi(yloc, 'left')
    labelx = x1;
    textanchor = 'right';
    coor1offset = -0.1;
    loganchor = 'right';
else
    labelx = x2;
    textanchor = 'left';
    coor1offset = 0.1;
    loganchor = 'left';
end
if ~isempty(yticklabels)
    fprintf(fp, '  \\begin{pgfscope}\n');
    specify_pgf_color(fp, '    ', 'fill', ycolor);
	multiplier = [];
	for i = 1:length(ytick)
        y = ytick(i);
        coor = norm2abs([labelx, y]);
        label = trim_whitespace(yticklabels(1+mod(i-1,length(yticklabels)),:));
        if iscell(label)
            label = cell2mat(label);
        end
        if ~isLogScale
            label_num = str2double(label);
            if ~isempty(label_num) && (label_num ~= 0)
                multiplier = [multiplier ytick_data(i)/label_num];
            end
            fprintf(fp,'    \\pgftext[x=%gcm,y=%gcm,%s]{%s%s%s}\n', ...
                coor(1)+coor1offset, coor(2), textanchor, matfig2pgf_opt.mathprecode, ...
                label, matfig2pgf_opt.mathpostcode);
        else
            fprintf(fp,'    \\pgftext[x=%gcm,y=%gcm,%s]{%s10^{%s}%s}\n', ...
                coor(1)+coor1offset, coor(2), textanchor, matfig2pgf_opt.mathprecode, ...
                label, matfig2pgf_opt.mathpostcode);
        end
	end
	if ~isLogScale && (abs(mean(multiplier)-1) > 1e-10) && strcmpi(get(handle,'YTickLabelMode'), 'auto')
        coor = norm2abs([labelx y2]);
        fprintf(fp,'   \\pgftext[x=%gcm,y=%gcm,bottom,%s]{%s\\times 10^{%g}%s}\n', ...
            coor(1)+coor1offset, coor(2)+0.1, loganchor, matfig2pgf_opt.mathprecode, ...
            log10(mean(multiplier)), matfig2pgf_opt.mathpostcode);
	end
    fprintf(fp, '  \\end{pgfscope}\n');
end
if (~isempty(texticklabelsize)) && (~strcmp(texticklabelsize, 'normalsize'))
    fprintf(fp, '  }\n');
end

%-- end of draw_tick_labels ------------------------------------------



%% FUNCTION DRAW_TICK_MARKS
%
%    Draw the tick marks
%
%    draw_tick_marks( handle, fp )
%
%--------------------------------------------------------------------------
function draw_tick_marks( handle, fp )

global matfig2pgf_opt;

% DRAW TICK MARKS
ticklength = get(handle, 'TickLength');
ticklength = ticklength(1);
linewidth = max(get(handle, 'LineWidth'), matfig2pgf_opt.minlinewidth);

position = get(handle, 'Position');
x1 = position(1);
x2 = x1+position(3);
y1 = position(2);
y2 = y1+position(4);
coor = norm2abs([x1 x2 ; y1 y2]);

showBox = get(handle, 'Box');
showBox = strcmp(showBox, 'on');
xAxisLocation = get(handle, 'XAxisLocation');
yAxisLocation = get(handle, 'YAxisLocation');
xcolor = get(handle, 'XColor');
ycolor = get(handle, 'YColor');

% Draw the tick marks along x-axis
fprintf(fp, '  %% X tick marks\n');
fprintf(fp, '  \\begin{pgfscope}\n');
fprintf(fp, '    \\pgfsetlinewidth{%.1fpt}\n', linewidth);
specify_pgf_color(fp, '    ', 'line', xcolor);
xtick = get_normalized_xtick(handle, showBox, false);
if ~isempty(xtick)
    fprintf(fp, '    \\foreach \\x in {');
    for x = xtick
        tempcoor = norm2abs([x;0]);
        fprintf(fp, '%g,', tempcoor(1));
    end
    fseek(fp, -1, 'cof');  % remove last comma
    fprintf(fp, '}\n    {\n');
    if showBox || strcmp(xAxisLocation, 'bottom')
        fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{\\x cm}{%gcm}}\\pgfpathlineto{\\pgfpoint{\\x cm}{%gcm}}\n',...
            coor(2,1), coor(2,1)+ticklength*(coor(2,2)-coor(2,1)));
    end
    if showBox || strcmp(xAxisLocation, 'top')
        fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{\\x cm}{%gcm}}\\pgfpathlineto{\\pgfpoint{\\x cm}{%gcm}}\n',...
            coor(2,2), coor(2,2)-ticklength*(coor(2,2)-coor(2,1)));
    end
    fprintf(fp, '    }\n');
end

% Draw minor ticks along x-axis
xtick_with_minors = get_normalized_xtick(handle, showBox, true);
if ~isempty(xtick_with_minors)
    if length(xtick_with_minors) > length(xtick)
        fprintf(fp, '    \\foreach \\x in {');
        for x = xtick_with_minors
            % Skip all major tick marks
            if find(x == xtick)
                continue;
            end
            tempcoor = norm2abs([x;0]);
            fprintf(fp, '%g,', tempcoor(1));
        end
        fseek(fp, -1, 'cof');  % remove last comma
        fprintf(fp, '}\n    {\n');
        if showBox || strcmp(xAxisLocation, 'bottom')
            fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{\\x cm}{%gcm}}\\pgfpathlineto{\\pgfpoint{\\x cm}{%gcm}}\n',...
                coor(2,1), coor(2,1)+0.5*ticklength*(coor(2,2)-coor(2,1)));
        end
        if showBox || strcmp(xAxisLocation, 'top')
            fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{\\x cm}{%gcm}}\\pgfpathlineto{\\pgfpoint{\\x cm}{%gcm}}\n',...
                coor(2,2), coor(2,2)-0.5*ticklength*(coor(2,2)-coor(2,1)));
        end
        fprintf(fp, '    }\n');
    end
end
fprintf(fp,'    \\pgfusepath{stroke}\n');
fprintf(fp,'  \\end{pgfscope}\n');

% Draw the tick marks along the y-axis
fprintf(fp, '  %% Y tick marks\n');
fprintf(fp, '  \\begin{pgfscope}\n');
fprintf(fp, '    \\pgfsetlinewidth{%.1fpt}\n', linewidth);
specify_pgf_color(fp, '    ', 'line', ycolor);
ytick = get_normalized_ytick(handle, showBox, true);
if ~isempty(ytick)
    fprintf(fp, '    \\foreach \\y in {');
    for y = ytick
        tempcoor = norm2abs([0;y]);
        fprintf(fp, '%g,', tempcoor(2));
    end
    fseek(fp, -1, 'cof');
    fprintf(fp, '}\n    {\n');
    if showBox || strcmp(yAxisLocation, 'left')
        fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{%gcm}{\\y cm}}\\pgfpathlineto{\\pgfpoint{%gcm}{\\y cm}}\n',...
            coor(1,1), coor(1,1)+ticklength*(coor(1,2)-coor(1,1)));
    end
    if showBox || strcmp(yAxisLocation, 'right')
        fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{%gcm}{\\y cm}}\\pgfpathlineto{\\pgfpoint{%gcm}{\\y cm}}\n',...
            coor(1,2), coor(1,2)-ticklength*(coor(1,2)-coor(1,1)));
    end
    fprintf(fp, '    }\n');
end

% Draw minor ticks along y-axis
ytick_with_minors = get_normalized_ytick(handle, showBox, true);
if ~isempty(ytick_with_minors)
    if length(ytick_with_minors) > length(ytick)
        fprintf(fp, '    \\foreach \\y in {');
        for y = ytick_with_minors
            % Skip all major tick marks
            if find(y == ytick)
                continue;
            end
            tempcoor = norm2abs([0;y]);
            fprintf(fp, '%g,', tempcoor(2));
        end
        fseek(fp, -1, 'cof');
        fprintf(fp, '}\n    {\n');
        if showBox || strcmp(yAxisLocation, 'left')
            fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{%gcm}{\\y cm}}\\pgfpathlineto{\\pgfpoint{%gcm}{\\y cm}}\n',...
                coor(1,1), coor(1,1)+0.5*ticklength*(coor(1,2)-coor(1,1)));
        end
        if showBox || strcmp(yAxisLocation, 'right')
            fprintf(fp, '      \\pgfpathmoveto{\\pgfpoint{%gcm}{\\y cm}}\\pgfpathlineto{\\pgfpoint{%gcm}{\\y cm}}\n',...
                coor(1,2), coor(1,2)-0.5*ticklength*(coor(1,2)-coor(1,1)));
        end
        fprintf(fp, '    }\n');
    end
end
fprintf(fp,'    \\pgfusepath{stroke}\n');
fprintf(fp,'  \\end{pgfscope}\n');

%-- end of function draw_tick_marks ---------------------------------------



%% FUNCTION DRAW_TEXT
%
%    draw_text( handle, fp )
%
%    handle - handle to the text object
%    fp     - file identifier
%--------------------------------------------------------------------------
function draw_text( handle, fp )

global matfig2pgf_opt;

if ~strcmp(get(handle, 'Visible'), 'on')
    return
end

% It is possible to defined a different text for usage in LaTeX. This
% different text can be defined in the 'UserData' property of the text.
% This property must contain the field "matfig2pgf_text: <text>".
text = '';
userdata = get(handle, 'UserData');
if ischar(userdata) && ~isempty(userdata)
    fprintf('UserData: %s\n', userdata);
    match = regexp(userdata, 'matfig2pgf_text\s*:\s*(.*)', 'tokenExtents', 'once');
    if ~isempty(match)
        text = userdata(match(1):match(2));
    end
end
if length(text) <= 0
    text = get(handle, 'String');
    if iscell(text)
        text = cell2mat(text);
    end
    if length(trim_whitespace(text)) <= 0
        return
    end
    if matfig2pgf_opt.converttexttolatex
        text = convert_text_to_latex(text);
    end
end

parent_handle = get_parent_axes(handle);
rotation = get(handle, 'Rotation');
halign = get(handle, 'HorizontalAlignment');
valign = get(handle, 'VerticalAlignment');
units = get(handle, 'Units');
color = get(handle, 'Color');

% Get coordinates and convert them to absolute coordinates
set(handle, 'Units', 'data');
position = get(handle, 'Position');
set(handle, 'Units', units);
coor = data2norm(parent_handle, transpose(position(1:2)));
coor = norm2abs(coor);

% Text alignment
switch halign,
    case 'center'
        halign = '';
end
switch valign,
    case 'cap'
        valign = 'top';
    case 'baseline'
        valign = 'base';
    case 'middle'
        valign = '';
end
if (~isempty(valign)) && (~isempty(halign))
    alignText = sprintf('%s,%s,', valign, halign);
elseif ~isempty(valign)
    alignText = sprintf('%s,', valign);
elseif ~isempty(halign)
    alignText = sprintf('%s,', halign);
else
    alignText = '';
end

specify_pgf_color(fp, '    ', 'fill', color);
textextlabelsize = matfig2pgf_opt.textextlabelsize;
if (~isempty(textextlabelsize)) && (~strcmp(textextlabelsize, 'normalsize'))
    fprintf(fp, ['  {\\' textextlabelsize '\n']);
end    
fprintf(fp, '    \\pgftext[%sx=%gcm,y=%gcm,rotate=%g]{%s%s%s}\n', ...
    alignText, coor(1), coor(2), rotation, matfig2pgf_opt.textprecode, ...
    text, matfig2pgf_opt.textpostcode);
if (~isempty(textextlabelsize)) && (~strcmp(textextlabelsize, 'normalsize'))
    fprintf(fp, '  }\n');
end

%-- end of function draw_text ---------------------------------------------



%% FUNCTION CONVERT_TEXT_TO_LATEX
%
%    textout = convert_text_to_line(textin)
%
%       textin  - normal text to be converted to proper latex code
%
%       textout - converted text
%--------------------------------------------------------------------------
function textout = convert_text_to_latex(textin)

global matfig2pgf_opt;

% Split strings in groups separated by space and/or }[a-z]
splitStrings = {};
i = 1;
thisStartIndex = 1;
while i <= length(textin),
    if ~isempty(regexp(textin(i), '\s', 'once'))
        splitStrings{length(splitStrings)+1} = textin(thisStartIndex:i);
        thisStartIndex = i+1;
    elseif (i < length(textin)) && ~isempty(regexpi(textin(i:i+1), '}[a-z]', 'once'))
        splitStrings{length(splitStrings)+1} = textin(thisStartIndex:i);
        thisStartIndex = i+1;
    elseif (i < length(textin)) && ~isempty(regexpi(textin(i:i+1), '[^_\^]{'))
        splitStrings{length(splitStrings)+1} = textin(thisStartIndex:i);
        thisStartIndex = i+1;        
    elseif i == length(textin)
        % Last character of string
        splitStrings{length(splitStrings)+1} = textin(thisStartIndex:i);
    end
    i = i+1;
end

% If two consecutive strings need to set in mathmode and have no whitespace
% in between. They must be joined to one math mode string.
newSplitStrings = {};
for i = 1:length(splitStrings)
    if i > 1
        prev = newSplitStrings{length(newSplitStrings)};
        next = splitStrings{i};
        if inMathMode(prev) && inMathMode(next)
            if isempty(regexp(prev(end), '\s', 'once')) && isempty(regexp(next(1), '\s', 'once'))
                newSplitStrings{length(newSplitStrings)} = [prev next];
            else
                newSplitStrings{length(newSplitStrings)+1} = next;
            end
        else
            newSplitStrings{length(newSplitStrings)+1} = next;
        end
    else
        newSplitStrings{length(newSplitStrings)+1} = splitStrings{i};
    end
end
splitStrings = newSplitStrings;

textout = '';
for i = 1:length(splitStrings)
    if ~inMathMode(splitStrings{i})
        textout = [textout splitStrings{i}];
    else
        thisString = splitStrings{i};
        
        % Remove whitespace at end of string
        lastIndex = length(thisString)+1-regexp(fliplr(thisString), '[^\s]', 'once');
        if lastIndex < length(thisString)
            trailingWhitespace = true;
            thisString = thisString(1:lastIndex);
        else
            trailingWhitespace = false;
        end
        
        % If the are acculades at the beginning and end they can be removed
        if strcmp(thisString(1), '{') && strcmp(thisString(lastIndex), '}')
            thisString = thisString(2:lastIndex-1);
        end

        textout = [textout matfig2pgf_opt.mathprecode thisString ...
            matfig2pgf_opt.mathpostcode];
        if trailingWhitespace
            textout = [textout ' '];
        end
    end
end

% Replace % signs in the text because they are comments in latex
textout = regexprep(textout, '%', '\\%');
%-- end of function convert_text_to_latex ---------------------------------



%% FUNCTION INMATHMODE
%
% Determines whether a string needs to be typeset in math mode in LaTeX
%
% [ mathmode ] = inMathMode( str )
%
%  str - string that needs to be checked
%
%  mathmode - True when it needs to be typeset in math mode, return false
%             when it should be typeset in normal text mode.
function [ mathmode ] = inMathMode( str )
  mathmode = ~isempty(regexp(str, '[|\\_\^]', 'once'));
%-- end of function inmathmode --------------------------------------------



%% FUNCTION DRAW_LINE
%
%    draw_line(handle, fp)
%
%      handle - handle to the line object
%      fp     - file identifier where the pgf commands must be written
%--------------------------------------------------------------------------
function draw_line( handle, fp )

if ~strcmp(get(handle, 'Visible'), 'on')
    return
end

global matfig2pgf_opt;

axes_handle = get_parent_axes(handle);
position = get(axes_handle, 'Position');
axes_x1 = position(1);
axes_x2 = axes_x1+position(3);
axes_y1 = position(2);
axes_y2 = axes_y1+position(4);
axes_coor = norm2abs([axes_x1 axes_x2 ; axes_y1 axes_y2]);
axes_widthheight = norm2abs(transpose(position(3:4)));

xdata = get(handle, 'XData');
fprintf('Drawing line with %d points\n', length(xdata));
ydata = get(handle, 'YData');
normcoor = data2norm(axes_handle, [xdata;ydata]);

visible_groups = find_visible_groups(normcoor, axes_handle);

fprintf(fp, '  %% Line\n');
if matfig2pgf_opt.includedraftcode
    fprintf(fp, '  \\makeatletter\\ifpgf@draftmode\\makeatother\\else\n');
end
fprintf(fp, '  \\begin{pgfscope}\n');
fprintf(fp, '    \\pgfpathrectangle{\\pgfpoint{%gcm}{%gcm}}{\\pgfpoint{%gcm}{%gcm}}\n', ...
    axes_coor(1,1), axes_coor(2,1), axes_widthheight(1,1), axes_widthheight(2,1));
fprintf(fp, '    \\pgfusepath{clip}\n');

% Draw all line parts (groups of points(
for gr = visible_groups
    group = cell2mat(gr);
    draw_line_part(fp, normcoor(:,group.start:group.end), handle);
end

fprintf(fp, '  \\end{pgfscope}\n');
if matfig2pgf_opt.includedraftcode
    fprintf(fp, '  \\fi\n');
end

%- end of function draw_line ----------------------------------------------



%% FUNCTION FIND_VISIBLE_GROUPS
%
%    [ visible_groups ] = find_visible_groups(normcoor, axes_handle)
%
%      normcoor    - normalized coordinates
%      axes_handle - handle of axis object
%
%--------------------------------------------------------------------------
function [ visible_groups ] = find_visible_groups(normcoor, axes_handle)
% Get bounding box in normalized coordinates.
xlim = data2norm_x(axes_handle, get(axes_handle, 'XLim'));
if strcmp(get(axes_handle, 'XDir'), 'reverse')
    xlim = fliplr(xlim);
end
ylim = data2norm_y(axes_handle, get(axes_handle, 'YLim'));
if strcmp(get(axes_handle, 'YDir'), 'reverse')
    ylim = fliplr(ylim);
end

% Check which lines cross the left border of the axes bounding box
leftOfAxis = normcoor(1,:) < xlim(1);
tmp = find(diff(leftOfAxis) ~= 0);
crossingLeft = [];
for i = tmp
    ycross = (xlim(1) - normcoor(1, i)) / (normcoor(1, i + 1) - normcoor(1, i)) ...
        * (normcoor(2, i + 1) - normcoor(2, i)) + normcoor(2, i);
    if (ycross >= ylim(1)) && (ycross <= ylim(2))
        crossingLeft(end + 1) = i; %#ok<AGROW>
    end
end

% Check which lines cross the right border of the axes bounding box
rightOfAxis = normcoor(1,:) > xlim(2);
tmp = find(diff(rightOfAxis) ~= 0);
crossingRight = [];
for i = tmp
    ycross = (xlim(1) - normcoor(1, i)) / (normcoor(1, i + 1) - normcoor(1, i)) ...
        * (normcoor(2, i + 1) - normcoor(2, i)) + normcoor(2, i);
    if (ycross >= ylim(1)) && (ycross <= ylim(2))
        crossingRight(end + 1) = i; %#ok<AGROW>
    end
end

% Check which lines cross the bottom border of the axes bounding box
belowAxis = normcoor(2,:) < ylim(1);
tmp = find(diff(belowAxis) ~= 0);
crossingBottom = [];
for i = tmp
    xcross = (ylim(1) - normcoor(2, i)) / (normcoor(2, i + 1) - normcoor(2, i)) ...
        * (normcoor(1, i + 1) - normcoor(1, i)) + normcoor(1, i);
    if (xcross >= xlim(1)) && (xcross <= xlim(2))
        crossingBottom(end + 1) = i; %#ok<AGROW>
    end
end

% Check which lines cross the bottom border of the axes bounding box
aboveAxis = normcoor(2,:) > ylim(2);
tmp = find(diff(aboveAxis) ~= 0);
crossingTop = [];
for i = tmp
    xcross = (ylim(1) - normcoor(2, i)) / (normcoor(2, i + 1) - normcoor(2, i)) ...
        * (normcoor(1, i + 1) - normcoor(1, i)) + normcoor(1, i);
    if (xcross >= xlim(1)) && (xcross <= xlim(2))
        crossingTop(end + 1) = i; %#ok<AGROW>
    end
end

crossing = union(union(union(crossingLeft, crossingRight), crossingBottom), ...
    crossingTop);
notanumbers = sum(isnan(normcoor));
nandiff = find(diff(notanumbers) ~= 0);
outsideAxis = leftOfAxis + rightOfAxis + belowAxis + aboveAxis;

visible_groups = {};
if notanumbers(1)
    startIdx = nan;
else
    startIdx = 1;
end
N = size(normcoor, 2);
crossing(end + 1) = N;
crossPtr = 1;
nandiff(end + 1) = N;
nanPtr = 1;
while true
    if (crossing(crossPtr) == N) && (nandiff(nanPtr) == N)
        break;
    end
    if crossing(crossPtr) < nandiff(nanPtr)
        % The first crossing is a crossing of the axis bounding box
        idx = crossing(crossPtr);
        crossPtr = crossPtr + 1;
        if outsideAxis(idx) && outsideAxis(idx + 1)
            % we went from outside to outside (crossing through axis)
            group = [idx idx + 1];
            startIdx = nan;        
        elseif outsideAxis(idx)
            % we went from outside to inside
            group = nan;
            startIdx = idx;
        else
            % we went from inside to outside
            group = [startIdx idx + 1];
            startIdx = nan;
        end
    else
        % The first crossing is a crossing from nan to a number or the
        % other way around
        idx = nandiff(nanPtr);
        nanPtr = nanPtr + 1;
        if isnan(startIdx)
            % we went from nan to valid number
            startIdx = idx + 1;
            group = nan;
        else
            % we went from a valid number to nan
            % Check whether the group is inside or outside the axis
            if outsideAxis(idx)
                group = nan;
            else
                group = [startIdx idx];
            end
            startIdx = nan;
        end
    end
    % Add the group to the list if it is visible.
    if ~isnan(group)
        if ~isempty(visible_groups) && (visible_groups{end}.end >= (group(1) - 1))
            % This group adjecent to the previous group
            visible_groups{end}.end = group(2); %#ok<AGROW>
        else
            visible_groups(end + 1) = {struct('start', group(1), 'end', group(2))}; %#ok<AGROW>
        end
    end
end
if ~isnan(startIdx)
        if ~isempty(visible_groups) && (visible_groups{end}.end >= (startIdx - 1))
            % This group adjecent to the previous group
            visible_groups{end}.end = N; %#ok<AGROW>
        else
            visible_groups(end + 1) = {struct('start', startIdx, 'end', N)}; %#ok<AGROW>
        end
end

%%- end of function find_visible_groups -----------------------------------



%% FUNCTION DRAW_LINE_PART
%
%    draw_line_part(fp, xdata, ydata, xlim, ylim, position, handle)
%
%      fp       - file identifier
%      xydata   - array with x- and y-coordinates of the line part that
%                 needs to be drawn
%      handle   - handle to line object
%--------------------------------------------------------------------------
function draw_line_part (fp, xydata, handle)

global matfig2pgf_opt;

linestyle = get(handle, 'LineStyle');
marker = get(handle, 'Marker');
linewidth = max(get(handle, 'LineWidth'), matfig2pgf_opt.minlinewidth);

xydata = norm2abs( xydata );
if strcmp(marker, 'none')
    % Calculate maximum error of reduced line, convert linewidth in pt to cm
    maxerror = matfig2pgf_opt.reducedlineerror * linewidth * (1/72*2.54);
    xydata = reduce_line(xydata, maxerror);
end

% Make sure that values are not too large or too small
xydata(1,:) = min(xydata(1,:), 2*matfig2pgf_opt.figwidth);
xydata(1,:) = max(xydata(1,:), -matfig2pgf_opt.figwidth);
xydata(2,:) = min(xydata(2,:), 2*matfig2pgf_opt.figheight);
xydata(2,:) = max(xydata(2,:), -matfig2pgf_opt.figheight);

% Draw the line itself
if ~strcmp(linestyle, 'none')
    color = get(handle, 'Color');
	
    fprintf(fp, '    %% Draw line part (line itself)\n');
	fprintf(fp, '    \\begin{pgfscope}\n');
    generate_linestyle_code(fp, linestyle, color, linewidth, 6);
	fprintf(fp, '      \\pgfsetroundjoin\n');
	fprintf(fp, '      \\pgfplothandlerlineto\n');
    generate_plotstream_code(fp, xydata, 6);
	fprintf(fp, '      \\pgfusepath{stroke}\n');
	fprintf(fp, '    \\end{pgfscope}\n');
end

% Draw plot marks (if necessary)
if ~strcmp(marker, 'none')
    color = get(handle, 'MarkerEdgeColor');
    if strcmp(color, 'auto')
        color = get(handle, 'Color');
    end

    bgcolor = get(handle, 'MarkerFaceColor');
    markersize = get(handle, 'MarkerSize');

    fprintf(fp, '    %% Draw line part (plot marks)\n');
    fprintf(fp, '    \\begin{pgfscope}\n');
    generate_markerstyle_code(fp, marker, color, linewidth, bgcolor, ...
        markersize, 6);
	generate_plotstream_code(fp, xydata, 6);
	fprintf(fp, '    \\end{pgfscope}\n');
end

%-- end of draw_line_part function ----------------------------------------



%% FUNCTION GENERATE_LINESTYLE_CODE
%
%  Create the code that draws a line
%
%  generate_linestyle_code( linestyle, linecolor, linewidth )
%  generate_linestyle_code( linestyle, linecolor, linewidth, indent )
%
%    fp        - file pointer
%    linestyle - style of the line ('-' solid, '--' dashed, etc...)
%    linecolor - array width rgb values (0-1), color of the line
%    linewidth - width if the line in points
%    indent    - integer, number of spaces to prepend to each line
%
%--------------------------------------------------------------------------
function generate_linestyle_code( fp, linestyle, linecolor, linewidth, ...
    varargin )

% Create the indent with spaces
if nargin > 4
    indent = '';
    for i = 1:varargin{1}
        indent = [indent ' '];
    end
else
    indent = '';
end

% Make sure that the line width is no less than the minimum line width
global matfig2pgf_opt;
linewidth = max(linewidth, matfig2pgf_opt.minlinewidth);

fprintf(fp, '%s\\pgfsetlinewidth{%.2fpt}\n', indent, linewidth);
specify_pgf_color(fp, indent, 'line', linecolor);
switch linestyle
    case '-'
        fprintf(fp, '%s\\pgfsetdash{}{0pt}\n', indent);
    case '--'
        fprintf(fp, '%s\\pgfsetdash{{%.2fpt}{%.2fpt}}{0pt}\n', ...
            indent, 3*linewidth, linewidth);
    case ':'
        fprintf(fp, '%s\\pgfsetdash{{%.2fpt}{%.2fpt}}{0pt}\n', ...
            indent, linewidth, linewidth);
    case '-.'
        fprintf(fp, '%s\\pgfsetdash{{%.2fpt}{%.2fpt}{%.2fpt}{%.2fpt}}{0pt}\n', ...
            indent, linewidth, 2*linewidth, 3*linewidth, 2*linewidth);
end

%- end of function generate_linestyle_code --------------------------------



%% FUNCTION GENERATE_MARKERSTYLE_CODE
%
%  generate_markerstyle_code( fp, marker, markerEdgeColor, markerFaceColor, markerSize)
%  generate_markerstyle_code( fp, marker, markerEdgeColor, markerFaceColor, markerSize, indent )
%
%    fp              - file pointer
%    marker          - string, marker type (i.e. 'o', '+', '*', '.', ...)
%    markeredgecolor - array with rgb values (0-1), color of the marker's
%                      edge
%    markeredgewidth - scalar, width in points of the edge line of the
%                      marker
%    markerfacecolor - array with rgb values (0-1) or 'none', background
%                      color of the marker
%    markersize      - scalar, maker size in points
%    indent          - integer, number of spaces prepended to each line
%                      (default: 0)
%--------------------------------------------------------------------------
function generate_markerstyle_code( fp, marker, markeredgecolor, ...
    markeredgewidth, markerfacecolor, markersize, varargin )

% Create the indent with spaces
if nargin > 4
    indent = '';
    for i = 1:varargin{1}
        indent = [indent ' '];
    end
else
    indent = '';
end

if strcmp(marker, '.')
    markersize = markersize / 3;
end

% Create the pgf code to draw the marker. Store the code that draws a
% marker in the markercode variable
draw_bg = false;
switch marker
    case '+'
        markercode = sprintf('\\pgfpathmoveto{\\pgfpoint{%.2fpt}{0pt}}', -0.5*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{0pt}}', markercode, 0.5*markersize);
        markercode = sprintf('%s\\pgfpathmoveto{\\pgfpoint{0pt}{%.2fpt}}', markercode, -0.5*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{0pt}{%.2fpt}}', markercode, 0.5*markersize);
    case 'o'
        markercode = sprintf('\\pgfpathcircle{\\pgfpointorigin}{%.2fpt}', 0.5*markersize);
        draw_bg = true;
    case '*'
        markercode = sprintf('\\pgfpathmoveto{\\pgfpoint{%.2fpt}{0pt}}', -0.5*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{0pt}}', markercode, 0.5*markersize);
        markercode = sprintf('%s\\pgfpathmoveto{\\pgfpoint{0pt}{%.2fpt}}', markercode, -0.5*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{0pt}{%.2fpt}}', markercode, 0.5*markersize);
        markercode = sprintf('%s\\pgfpathmoveto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, -0.25*sqrt(2)*markersize, -0.25*sqrt(2)*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, 0.25*sqrt(2)*markersize, 0.25*sqrt(2)*markersize);
        markercode = sprintf('%s\\pgfpathmoveto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, -0.25*sqrt(2)*markersize, 0.25*sqrt(2)*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, 0.25*sqrt(2)*markersize, -0.25*sqrt(2)*markersize);
    case '.'
        markercode = sprintf('\\pgfpathcircle{\\pgfpointorigin}{%.2fpt}', 0.5*markersize/3);
        markerfacecolor = markeredgecolor;
        draw_bg = true;
    case 'x'
        markercode = sprintf('\\pgfpathmoveto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            -0.25*sqrt(2)*markersize, -0.25*sqrt(2)*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, 0.25*sqrt(2)*markersize, 0.25*sqrt(2)*markersize);
        markercode = sprintf('%s\\pgfpathmoveto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, -0.25*sqrt(2)*markersize, 0.25*sqrt(2)*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, 0.25*sqrt(2)*markersize, -0.25*sqrt(2)*markersize);
    case 'square'
        markercode = sprintf('\\pgfpathrectangle{\\pgfpoint{%.2fpt}{%.2fpt}}{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            -0.5*markersize, -0.5*markersize, markersize, markersize);
        draw_bg = true;
    case 'diamond'
        markercode = sprintf('\\pgfpathmoveto{\\pgfpoint{%.2fpt}{0pt}}', ...
            -0.5*2/3*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{0pt}{%.2fpt}}', ...
            markercode, 0.5*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{0pt}}', ...
            markercode, 0.5*2/3*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{0pt}{%.2fpt}}', ...
            markercode, -0.5*markersize);
        markercode = sprintf('%s\\pgfpathclose', markercode);
        draw_bg = true;
    case {'^','v','>','<'}
        switch marker
            case '^'
                a = -pi/6;
            case 'v'
                a = pi/6;
            case '<'
                a = pi/3;
            case '>'
                a = 0;
        end
        markercode = sprintf('\\pgfpathmoveto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            cos(a)*0.5*markersize, sin(a)*0.5*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, cos(a+2*pi/3)*0.5*markersize, sin(a+2*pi/3)*0.5*markersize);
        markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            markercode, cos(a+4*pi/3)*0.5*markersize, sin(a+4*pi/3)*0.5*markersize);
        markercode = sprintf('%s\\pgfpathclose', markercode);
        draw_bg = true;
    case 'pentagram'
        a = pi/2;
        da = 2*pi/10;
        markercode = sprintf('\\pgfpathmoveto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            cos(a)*0.5*markersize, sin(a)*0.5*markersize);
        for i = a+da:2*da:2*pi+a-da;
            markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
                markercode, cos(i)*0.19*markersize, sin(i)*0.19*markersize);
            markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
                markercode, cos(i+da)*0.5*markersize, sin(i+da)*0.5*markersize);
        end
        markercode = sprintf('%s\\pgfpathclose', markercode);
        draw_bg = true;
    case 'hexagram'
        a = pi/2;
        da = 2*pi/12;
        markercode = sprintf('\\pgfpathmoveto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
            cos(a)*0.5*markersize, sin(a)*0.5*markersize);
        for i = a+da:2*da:2*pi+a-da;
            markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
                markercode, cos(i)*0.29*markersize, sin(i)*0.29*markersize);
            markercode = sprintf('%s\\pgfpathlineto{\\pgfpoint{%.2fpt}{%.2fpt}}', ...
                markercode, cos(i+da)*0.5*markersize, sin(i+da)*0.5*markersize);
        end
        markercode = sprintf('%s\\pgfpathclose', markercode);
        draw_bg = true;
    otherwise
        disp(marker)
end
if strcmp(markerfacecolor, 'none')
    draw_bg = false;
end

fprintf(fp, '%s\\pgfsetlinewidth{%.2fpt}\n', indent, markeredgewidth);
specify_pgf_color(fp, indent, 'line', markeredgecolor);

if draw_bg
    specify_pgf_color(fp, indent, 'fill', markerfacecolor);
    fprintf(fp, '%s\\pgfplothandlermark{%s\\pgfusepath{stroke,fill}}\n', ...
        indent, markercode);
else
    fprintf(fp, '%s\\pgfplothandlermark{%s\\pgfusepath{stroke}}\n', ...
        indent, markercode);
end

%- end of function generate_markerstyle_code --------------------------------



%% FUNCTION GENERATE_PLOTSTREAM_CODE
%
%  Generate a plot stream using xy-coordinates
%
%  generate_plotstream_code( fp, xydata )
%  generate_plotstream_code( fp, xydata, indent )
%
%    fp     - file pointer
%    xydata - array with x- and y-coordinates
%    indent - integer, number of spaces prepended to each line (default: 0)
%
%--------------------------------------------------------------------------
function generate_plotstream_code( fp, xydata, varargin )

% Create the indent with spaces
if nargin > 4
    indent = '';
    for i = 1:varargin{1}
        indent = [indent ' '];
    end
else
    indent = '';
end

if size(xydata,2) < 1
    return
end

fprintf(fp, '%s\\pgfplotstreamstart\n', indent);
fprintf(fp, '%s\\foreach \\x/\\y in {', indent);

if all( isfinite(xydata(:,1)) )
    fprintf(fp, '%.3f/%.3f', xydata(1,1), xydata(2,1));
end
for i = 2:size(xydata,2)
    if any(~isfinite(xydata(:,i)))
        continue
    end
    fprintf(fp, ',%.3f/%.3f', xydata(1,i), xydata(2,i));
    if rem(i, 10) == 9
        fprintf(fp, '\n');
    end
end

fprintf(fp, '}\n%s{\n', indent);
fprintf(fp, '%s\\pgfplotstreampoint{\\pgfpoint{\\x cm}{\\y cm}}\n', indent);
fprintf(fp, '%s}\n', indent);
fprintf(fp, '%s\\pgfplotstreamend\n', indent);

%- end of function generate_plotstream_code -------------------------------



%% FUNCTION REDUCE_LINE
%
%  Remove points that hardly changes the appearance of the line. This will
%  decrease the file size and increase LaTeX compilation time.
%
%  [ reduced_xydata ] = reduce_line( xydata )
%
%      xydata   - array with absolute x- and y-coordinates of the line
%                 part that needs to be drawn (in centimeters)
%
%      reduced_xydata - array with reduced number of normalized x- and
%                       y-coordinates (in centimeters)
%
%--------------------------------------------------------------------------
function [ reduced_xydata ] = reduce_line( xydata, maxerror )

N = size(xydata,2);

if (maxerror <= 0) || (N < 3)
    reduced_xydata = xydata;
    return
end

xydata = minmaxdecimation(xydata, 4*maxerror);

N = size(xydata,2);

plotPoints = 1;
lastPlotted = 1;

xdata = xydata(1,:);
ydata = xydata(2,:);

for i = 3:N
    % Calculate distance
    % see http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
    % x1 = xydata(:,i)  x2 = xydata(:,lastPlotted)  x0 = all points
    % between x1 and x2
    % See also: http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
    p1 = xydata(:,i);
    p2 = xydata(:,lastPlotted);
    dp = sqrt(sum((p1-p2).^2));
    frac1 = ( xdata(lastPlotted+1:i-1)-xdata(i) ) .* ( xdata(lastPlotted)-xdata(i) );
    frac1 = frac1 + ( ydata(lastPlotted+1:i-1)-ydata(i) ) .* ( ydata(lastPlotted)-ydata(i) );
    u = frac1 ./ sum((p2-p1).^2);
    
    % Convert u to the distance from the point to p1 or p2
    % For points where the closest point on line p1-p2 is outside of p1 and
    % p2 u is now the distance to p1 or p2. When the closest point was
    % between p1 and p2 u will be zero.
    u((u > 0) & (u <= 1)) = 0;
    u(u>1) = dp*(u(u>1)-1);
    u(u<0) = dp*(-u(u<0));

    % Calculate shortest distance from point to line (p1-p2)
    a = xdata(lastPlotted)-xdata(i);
    b = ydata(lastPlotted)-ydata(i);
    c = xdata(i)-xdata(lastPlotted+1:i-1);
    d = ydata(i)-ydata(lastPlotted+1:i-1);
    frac1 = abs(a.*d-b.*c);
    frac2 = sqrt(sum((xydata(:,lastPlotted)-xydata(:,i)).^2));
    d = frac1./frac2;

    d = sqrt(d.^2 + u.^2);

    if max(d) > maxerror
        lastPlotted = i-1;
        plotPoints = [plotPoints lastPlotted];
    end
    
end

plotPoints = [plotPoints N];
if N > 5
    reduced_xydata = xydata(:,plotPoints);
else
    reduced_xydata = xydata;
end
if N ~= size(reduced_xydata,2)
    fprintf('  reduced number of data points: original: %d -> reduced: %d\n',...
        N, size(reduced_xydata,2));
end

%-- end of function reduce_line -------------------------------------------



%% FUNCTION MINMAXDECIMATION
%
%  If the number of data points is (much) larger than viewable on screen or
%  paper. On paper the linewidth is the limiting factor. E.g. a line going
%  up and down ten times within the width of the line will only show a
%  single line.
%  Note: this method only works correctly if the x-values are strictly
%  non-decreasing or non-increasing.
%
%  ( decimatedXydata ) = minmaxdecimation( xydata, columnWidth )
%
%    xydata      - array with the x- and y-coordinates
%    columnWidth - width of a "colum" that only the minimum and maximum
%                  coordinate and not all data points (typically something
%                  like 0.25 * linewidth)
%
function [ decimatedXydata ] = minmaxdecimation( xydata, columnWidth )

xdata = xydata(1,:);
ydata = xydata(2,:);
minx = min(xdata);
maxx = max(xdata);
N = (maxx-minx)/columnWidth;  % number of columns

% dx = xdata(i+1)-xdata(i) is the same for all values. Otherwise this
% function is not going to work.
dx = diff(xdata);
maxdx = max(dx);
mindx = min(dx);
dx = median(dx);

% If dx is not the same for all values OR
% if the number of columns is less than 0.5*data length
% then we can not use min-max decimation
if ( (maxdx-mindx)/dx > 1e-6 ) || ( N > 0.5*length(xdata) )
    decimatedXydata = xydata;
    return;
end

decimatedXydata = [];
lastIndex = 0;
for i = 1:ceil(N)
    thisIndex = min( floor(i*columnWidth/dx), length(xdata) );
    [miny, minIndex] = min(ydata(lastIndex+1:thisIndex));
    [maxy, maxIndex] = max(ydata(lastIndex+1:thisIndex));
    minIndex = minIndex+lastIndex;
    maxIndex = maxIndex+lastIndex;
    if minIndex < maxIndex
        decimatedXydata = [decimatedXydata [xdata(minIndex);miny] [xdata(maxIndex);maxy]];
    else
        decimatedXydata = [decimatedXydata [xdata(maxIndex);maxy] [xdata(minIndex);miny]];
    end
    lastIndex = thisIndex;
end
if size(decimatedXydata,2) > 10
    fprintf('  min-max decimation: original %d  decimated %d\n', size(xydata,2), size(decimatedXydata,2));
else
    decimatedXydata = xydata;
end
%- end of function minmaxdecimation ---------------------------------------



%% FUNCTION CHANGE_FONTS
%
%  Change the size and name of all texts
%
%  change_fonts( handle )
%
%    handle - handle to the figure object for which all fonts must be
%             changed
%
%--------------------------------------------------------------------------
function change_fonts( handle )

global matfig2pgf_opt;

texthandles = transpose(findobj(handle, 'Type', 'text'));
axeshandles = transpose(findobj(handle, 'Type', 'axes'));

for h = axeshandles
    if ~isempty(get(get(h, 'XLabel'), 'String') > 0)
        texthandles = [texthandles get(h, 'XLabel')];
    end
    if ~isempty(get(get(h, 'YLabel'), 'String') > 0)
        texthandles = [texthandles get(h, 'YLabel')];
    end
    if ~isempty(get(get(h, 'ZLabel'), 'String') > 0)
        texthandles = [texthandles get(h, 'ZLabel')];
    end
    if ~isempty(get(get(h, 'Title'), 'String') > 0)
        texthandles = [texthandles get(h, 'Title')];
    end
    
    set_and_remember(h, 'FontSize', matfig2pgf_opt.fontsize);
    set_and_remember(h, 'FontName', matfig2pgf_opt.fontname);
end

% In newer MATLAB version findobj(h, 'Type', 'text') will also return the
% Xlabels, Ylabels, Zlabels and titles of axes, while older version don't.
texthandles = unique(texthandles);

for h = texthandles
    set_and_remember(h, 'FontSize', matfig2pgf_opt.fontsize);
    set_and_remember(h, 'FontName', matfig2pgf_opt.fontname);
end

%-- end of function change_fonts ------------------------------------------



%% FUNCTION NORM2ABS
%
%  Convert normalized coordinates to absolute coordinates (of drawing
%  canvas)
%
%  [ abs_coor ] = norm2abs( norm_coor )
%
%    norm_coor - array with x- and y-coordinate. y-coordinate on row 1,
%                y-coordinates on row 2. E.g. norm_coor(5,2) is the y-
%                coordinate of the 5th coordinate.
%
%    abs_coor  - array with absolute (as on drawing canvas) coordinates
%
%--------------------------------------------------------------------------
function [ abs_coor ] = norm2abs( norm_coor )

% Make sure that the x-data is on the first row and the y-data on the
% second row
if (size(norm_coor,1) ~= 2) && (size(norm_coor,2) == 2)
    norm_coor = transpose(norm_coor);
end

global matfig2pgf_opt

mult = [ matfig2pgf_opt.figwidth 0 ; 0 matfig2pgf_opt.figheight ];
abs_coor = mult*norm_coor;

%-- end of function norm2abs ----------------------------------------------



%% FUNCTION DATA2NORM
%
%    [ norm_coor ] = data2norm( handle, data_coor )
%
%    handle    - handle to axes object
%    data_coor - array with x- and y-coordinates, row 1 contains the
%                x-coordinates and row 2 contains the y-coordinates
%
%    norm_coor    - array with normalized x- and y-coordinates
%--------------------------------------------------------------------------
function [ norm_coor, varargout ] = data2norm( handle, data_coor )

% Make sure that the x-data is on the first row and the y-data on the
% second row
if (size(data_coor,1) ~= 2) && (size(data_coor,2) == 2)
    data_coor = transpose(data_coor);
end

norm_x = data2norm_x(handle, data_coor(1,:));
norm_y = data2norm_y(handle, data_coor(2,:));

norm_coor = [ norm_x ; norm_y ];

%- end of function data2norm ----------------------------------------------



%% FUNCTION DATA2NORM_X
%
%    [ norm_x ] = data2norm( handle, data_x )
%
%    handle - handle to axes object
%    data_x - array with x-coordinates (as in data)
%
%    norm_x      - array with x-coordinates (normalized)
%--------------------------------------------------------------------------
function [ norm_x ] = data2norm_x ( handle, data_x )

position = get(handle, 'Position');
xlim = get(handle, 'XLim');

if strcmp(get(handle, 'XDir'), 'reverse')
    xlim = fliplr(xlim);
end

% Check whether the scale is logaritmic
if strcmp(get(handle, 'XScale'), 'log')
    negative_indices = find(data_x <= 0);
    for i = negative_indices
        data_x(i) = NaN;
    end
    xlim = log10(xlim);
    data_x = log10(data_x);
end
norm_x = position(1) + position(3) * (data_x-xlim(1))/(xlim(2)-xlim(1));


%- end of function data2norm_x --------------------------------------------



%% FUNCTION DATA2NORM_Y
%
%    [ norm_y ] = data2norm( handle, data_y )
%
%    handle - handle to axes object
%    data_y - array with data y-coordinates
%
%    norm_y      - array with normalized y-coordinates
%--------------------------------------------------------------------------
function [ norm_y ] = data2norm_y ( handle, data_y )

position = get(handle, 'Position');
ylim = get(handle, 'YLim');

if strcmp(get(handle, 'YDir'), 'reverse')
    ylim = fliplr(ylim);
end

if strcmp(get(handle, 'YScale'), 'log')
    negative_indices = find(data_y <= 0);
    for i = negative_indices
        data_y(i) = NaN;
    end
    ylim = log10(ylim);
    data_y = log10(data_y);
end

norm_y = position(2) + position(4) * (data_y-ylim(1))/(ylim(2)-ylim(1));

%- end of function data2norm_y --------------------------------------------



%% FUNCTION GET_NORMALIZED_XTICK
%
%    [ normalized_xtick ] = get_normalized_xtick( handle )
%    [ normalized_xtick ] = get_normalized_xtick( handle, no_limits )
%    [ normalized_xtick ] = get_normalized_xtick( handle, no_limits, includeMinors )
%
%    handle        - handle to the axes object
%    no_limits     - when this value is true, the xticks that are equal to
%                    xlim are not returned. (The first and last value in
%                    most situations). (default: false)
%    includeMinors - If true and the 'XMinorTick' propery is 'on', then
%                    minor ticks are included in the returned array. If
%                    false and/or the 'XMinorTick' property is 'off' the
%                    minor tick are not returned. (default: false)
% 
%    normalized_xtick - xtick property, but now normalized to figure
%--------------------------------------------------------------------------
function [ normalized_xtick ] = get_normalized_xtick( handle, varargin )

% no_limits
if nargin > 1
    no_limits = cell2mat(varargin(1));
else
    no_limits = false;
end

% includeMinors
if nargin > 2
    includeMinors = cell2mat(varargin(2));
else
    includeMinors = false;
end

xtick = get(handle, 'XTick');
xlim = get(handle, 'XLim');

if includeMinors
    if strcmp(get(handle, 'XMinorTick'), 'on') && strcmp(get(handle, 'XScale'), 'log') && ...
            (length(xtick) > 1)
        tickDiff = mean(diff(log10(xtick)));
        if tickDiff == 1
            current_tick = min(xlim);
            step = 10^floor(log10(current_tick));
            current_tick = ceil(current_tick/step)*step;
            while current_tick < max(xlim),
                xtick = [xtick current_tick];
                step = 10^floor(log10(current_tick));
                current_tick = current_tick + step;
            end
            xtick = unique(xtick);
        end
    end
end

% Remove ticks that are outside xlim (or equal to xlim if no_limits)
if no_limits
    xtick(xtick >= max(xlim) | xtick <= min(xlim)) = [];
else
    xtick(xtick > max(xlim) | xtick < min(xlim)) = [];
end

normalized_xtick = data2norm_x(handle, xtick);

%- end of function get_normalized_xtick -----------------------------------



%% FUNCTION GET_NORMALIZED_YTICK
%
%    [ normalized_ytick ] = get_normalized_ytick( handle )
%    [ normalized_ytick ] = get_normalized_ytick( handle, no_limits )
%    [ normalized_ytick ] = get_normalized_ytick( handle, no_limits, includeMinors )
% 
%    handle        - handle to the axes object
%    no_limits     - when this value is true, the xticks that are equal to
%                    xlim are not returned. (The first and last value in
%                    most situations). (default: false)
%    includeMinors - If true and the 'YMinorTick' propery is 'on', then
%                    minor ticks are included in the returned array. If
%                    false and/or the 'YMinorTick' property is 'off' the
%                    minor tick are not returned. (default: false)
%
%    normalized_ytick - ytick property, but now normalized to figure
%--------------------------------------------------------------------------
function [ normalized_ytick ] = get_normalized_ytick( handle, varargin )

if nargin > 1
    no_limits = cell2mat(varargin(1));
else
    no_limits = false;
end

% includeMinors
if nargin > 2
    includeMinors = cell2mat(varargin(2));
else
    includeMinors = false;
end

ytick = get(handle, 'YTick');
ylim = get(handle, 'YLim');

if includeMinors
    if strcmp(get(handle, 'YMinorTick'), 'on') && strcmp(get(handle, 'YScale'), 'log') && ...
            (length(ytick) > 1)
        tickDiff = mean(diff(log10(ytick)));
        if tickDiff == 1
            % Because Matlab does not provide information about how many minor
            % tick are drawn, we need to guess.
            current_tick = min(ylim);
            step = 10^floor(log10(current_tick));
            current_tick = ceil(current_tick/step)*step;
            while current_tick < max(ylim),
                ytick = [ytick current_tick];
                step = 10^floor(log10(current_tick));
                current_tick = current_tick + step;
            end
            ytick = unique(ytick);
        end
    end
end

% Remove ticks that are outside ylim (or equal to ylim if no_limits)
if no_limits
    ytick(ytick >= max(ylim) | ytick <= min(ylim)) = [];
else
    ytick(ytick > max(ylim) | ytick < min(ylim)) = [];
end

normalized_ytick = data2norm_y(handle, ytick);

%- end of function get_normalized_ytick -----------------------------------



%% FUNCTION TRIM_WHITESPACE
%
%  Remove whitespace at start and end of string
%
%  [ trimmed ] = trim_whitespace( str )
%
%    str - string that needs to be trimmed
%
%    trimmed - str without whitespace at start and end
%--------------------------------------------------------------------------
function [ trimmed ] = trim_whitespace( str )
trimmed = '';
for i = 1:length(str)
    if ~isspace(str(i))
        trimmed = str(i:length(str));
        break;
    end
end
for i = length(trimmed):-1:1
    if ~isspace(trimmed(i))
        trimmed = trimmed(1:i);
        break;
    end
end

%- end of function trim_whitespace ----------------------------------------



%% FUNCTION SET_AND_REMEMBER
%
% First the old propertyValue is stored (together with handle and
% propertyName) is the global matfig2pgf_props. Then the property is set to
% the new value.
%
% set_and_remember( handle, propertyName, propertyValue )
%
%--------------------------------------------------------------------------
function set_and_remember( handle, propertyName, propertyValue )
global matfig2pgf_props;

prop.handle = handle';
prop.name = propertyName;
prop.value = get(handle, propertyName);

i = length(matfig2pgf_props)+1;
matfig2pgf_props{i} = prop;

set(handle, propertyName, propertyValue);

%- end of set_and_remember ---------------------------------------



%% FUNCTION RESTORE_REMEMBERED_PROPERTIES
%
% Restore the properties in the global matfig2pgf_props. This global was
% earlier filled by the set_and_remember function.
%--------------------------------------------------------------------------
function restore_remembered_properties
global matfig2pgf_props

% Restored all properties in reversed order
n = length(matfig2pgf_props);
for i = n:-1:1
    prop = matfig2pgf_props{i};
    set(prop.handle, prop.name, prop.value);
end

%- end of function restore_remembered_properties --------------------------



%% FUNCTION GET_PARENT_AXES
%
%  Gets the handle to the parent object and checks whether it is an axes
%  object. If it isn't, get it's parent and check again. This is repeated
%  until an axes object is found. The handle to this axes object is
%  returned.
%
%--------------------------------------------------------------------------
function axesHandle = get_parent_axes( handle )

axesHandle = get(handle, 'Parent');
while ~strcmp(get(axesHandle, 'Type'), 'axes')
    axesHandle = get(axesHandle, 'Parent');
end

%- end of function get_parent_axes ----------------------------------------



%% FUNCTION SNAP_AXES_BORDERS
%
% Check all borders of all axes objects and snap them to the values defined
% in the option if they are within range.
%
%--------------------------------------------------------------------------
function snap_axes_borders( handle )

global matfig2pgf_opt;

snapdistance = matfig2pgf_opt.snapdistance;

axeshandles = get_axes_or_legend_handles(handle, 'axes');
for h = axeshandles
    % Make sure that the units of the axes are set to normalized
    if ~strcmp('normalized', get(h, 'Units'))
        set(h, 'Units', 'normalized');
    end
    
    pos = get(h, 'Position');
    newpos = pos;
    
    % Snap vertical borders
    snapleft = matfig2pgf_opt.snapleft * matfig2pgf_opt.fontsize / 72 ...
        * 2.54 / matfig2pgf_opt.figwidth;
    snapright = matfig2pgf_opt.snapright * matfig2pgf_opt.fontsize / 72 ...
        * 2.54 / matfig2pgf_opt.figwidth;
    snapright = 1 - snapright;
    left = pos(1);
    right = pos(1)+pos(3);
    
    if abs(snapleft-left) <= snapdistance
        left = snapleft;
    end
    if abs(snapright-right) <= snapdistance
        right = snapright;
    end
    newpos(1) = left;
    newpos(3) = right-left;
    
    % Snap horizontal borders
    snaptop = matfig2pgf_opt.snaptop * matfig2pgf_opt.fontsize / 72 ...
        * 2.54 / matfig2pgf_opt.figheight;
    if ~isempty(get(get(h, 'Title'), 'String'))
        snaptop = snaptop + 1;
    end
    snaptop = 1 - snaptop;
    snapbottom = matfig2pgf_opt.snapbottom * matfig2pgf_opt.fontsize / 72 ...
        * 2.54 / matfig2pgf_opt.figheight;
    top = pos(2)+pos(4);
    bottom = pos(2);
    
    if abs(snaptop-top) <= snapdistance
        top = snaptop;
    end
    if abs(snapbottom-bottom) <= snapdistance
        bottom = snapbottom;
    end
    newpos(2) = bottom;
    newpos(4) = top-bottom;
    
    % Set new position
    if max(abs(pos-newpos)) > 0
        set_and_remember(h, 'Position', newpos)
    end

end  % end of loop through all axes objects

%- end of function snap_axes_borders --------------------------------------



%% FUNCTION GET_AXES_OR_LEGEND_HANDLES
%
% Returns an array with all axes handles or all legend handles. Internally
% Matlab indentifies both as axes object, it needs some effort to figure
% out which is which.
%
% handles = get_axes_or_legend_handles(parentHandle, type)
%
%  parentHandle - handle
%  type         - string, either 'axes' or 'legends'
%
%--------------------------------------------------------------------------
function handles = get_axes_or_legend_handles(parentHandle, type)

% First get all axes handles, this includes the legends
allAxes = transpose(findobj(parentHandle, 'Type', 'axes'));
allLegends = [];
for h = allAxes
    allLegends = [allLegends legend(h)];
end
if strcmp(type, 'axes')
    handles = setdiff(allAxes, allLegends);
elseif strcmp(type, 'legends')
    handles = allLegends;
else
    handles = [];
end

%- end of function get_axes_handles


%% FUNCTION GET_PARENT_AXES_HANDLE
%
% Look up the parent of a handle and if it is an 'axes' return it.
% Otherwise check its parent and see if it is an 'axes'. Repeat this until
% and 'axes' is found or until a 'figure' is found. In the last case it
% will return -1
function axesHandle = get_parent_axes_handle(handle)
while true
    handle = get(handle, 'Parent');
    parentType = get(handle, 'Type');
    if strcmp(parentType, 'axes')
        axesHandle = handle;
        return;
    elseif strcmp(parentType, 'figure')
        axesHandle = -1;
        return;
    end
end

%- end of function get_parent_axes_handle


%% FUNCTION SPECIFY_PGF_COLOR
%
% Write PGF code to a file pointer to set a line or fill color.
function specify_pgf_color(fp, indent, type, colorspec)
if strcmpi(type, 'line')
    colorname = 'matfig2pgf_linecolor';
    pgfcmd = 'pgfsetstrokecolor';
elseif strcmpi(type, 'fill')
    colorname = 'matfig2pgf_fillcolor';
    pgfcmd = 'pgfsetfillcolor';
else
    error('Invalid type specified in function specify_pgf_color');
end
fprintf(fp,'%s\\definecolor{%s}{rgb}{%.3f,%.3f,%.3f}\n',...
    indent, colorname, colorspec(1), colorspec(2), colorspec(3));
fprintf(fp,'%s\\%s{%s}\n', ...
    indent, pgfcmd, colorname);

%- end of function specify_pgf_color

Contact us at files@mathworks.com