function matlab2pgfplots(varargin )
% matlab2pgfplots(outfile )
% matlab2pgfplots( outfile, OPTIONS )
%
% Generate LaTeX code for use in package pgfplots to
% draw line plots.
%
% It will use every (2d) line plot in the figure specified by handler fighandle.
%
% It understands
% - axis labels,
% - legends,
% - any 2d line plots,
% - line styles/markers (in case of styles=1),
% - tick positions, labels and axis limits (in case of axes=1).
%
% Linestyles and markers will follow as an option. However, pgfplots has its
% own line styles which may be appropriate.
%
% Although pgfplots can also handle bar and area plots, this script is not yet
% capable of converting them. Feel free to modify it and send the final version
% to me!
%
% OPTIONS are key value pairs. Known options are
% - 'fig',HANDLE
% a figure handle (default is 'gcf').
% - 'styles',0|1
% a boolean indicating whether line styles, markers and colors shall be exported (default 1).
% - 'axes',0|1
% a boolean indicating whether axis ticks, tick labels and limits shall be exported (default 0).
% - 'maxpoints',100000
% an integer denoting the maximum number of points exported to tex. If the actual number is larger,
% the data will be interpolated to 'maxpoints'. The interpolation assumes
% parametric plots if x and y are not monotonically increasing.
%
% See
% http://www.ctan.org/pkg/pgfplots
% for details about pgfplots.
%
%
%
% Copyright Christian Feuersaenger 2008
%
% This script requires Matlab version 7.4 (or above).
parser = inputParser;

parser.addRequired( 'outfile', @(x) ischar(x) );
parser.addParamValue( 'fig', gcf, @(x) ishandle(x) );
parser.addParamValue( 'styles', 1, @(x) x==0 || x==1 );
parser.addParamValue( 'axes' , 0, @(x) x==0 || x==1 );
parser.addParamValue( 'maxpoints', 100000, @(x) isnumeric(x) );

parser.parse( varargin{:} );

fighandle = parser.Results.fig;

lineobjs = findobj(fighandle, 'Type', 'line' );
axesobj = findobj( fighandle, 'Type', 'axes' );

% As far as I know, 'scatter' and 'scatter3' produce groups of this class:
scatterobjs = findobj(fighandle, 'Type', 'hggroup' );
lineobjs = [ lineobjs scatterobjs ];

legendobj = findobj( fighandle, 'tag', 'legend' );
if length(legendobj) > 0
allchildsoflegend = [ findobj( legendobj ) ];
lineobjs = setdiff( lineobjs, allchildsoflegend );
axesobj = setdiff( axesobj, allchildsoflegend );
end

FID=fopen( parser.Results.outfile, 'w' );
assert( FID >= 0, [ 'could not open file ' parser.Results.outfile ' for writing' ] );

ENDL=sprintf('\n');
TAB=sprintf('\t');
fwrite( FID, [ ...
'\begin{tikzpicture}%' ENDL ...
'\begin{axis}'] );

xislog = 0;
yislog = 0;

if length(axesobj) > 0
axis = axesobj(1);
xlabel = get( get(axis, 'XLabel'), 'String');
ylabel = get( get(axis, 'YLabel'), 'String');
zlabel = get( get(axis, 'ZLabel'), 'String');
xscale = get(axis,'XScale');
yscale = get(axis,'YScale');

axisoptions = {};
if length(xlabel) > 0
axisoptions = [ axisoptions [ 'xlabel={' xlabel '}'] ];
end
if length(ylabel) > 0
axisoptions = [ axisoptions ['ylabel={' ylabel '}'] ];
end
if strcmp(xscale,'log')
xislog=1;
axisoptions = [ axisoptions ['xmode=log'] ];
end
if strcmp(yscale,'log')
yislog = 1;
axisoptions = [ axisoptions ['ymode=log'] ];
end
if parser.Results.axes
for k = 'xy'
L = get(gca, [ k 'Lim'] );
axisoptions = [ axisoptions [ k 'min=' num2str(L(1)) ] ];
axisoptions = [ axisoptions [ k 'max=' num2str(L(2)) ] ];
end

for k = 'xy'
L = get(gca, [ k 'Tick'] );
opt = [ k 'tick={' ];
for q=1:length(L)
if q>1
opt = [opt ',' ];
end
opt = [opt num2str(L(q)) ];
end
opt = [ opt '}' ];
axisoptions = [axisoptions opt ];
end

end

axisoptstr = [];
for i = 1:length(axisoptions)
if i>1
axisoptstr = [axisoptstr ',' ENDL TAB];
end
axisoptstr = [axisoptstr axisoptions{i}];
end
if length( axisoptstr )
fwrite( FID, [ '[' ENDL TAB axisoptstr ']' ENDL ] );
end
end
fwrite( FID, ENDL );

if length(legendobj) > 0
legentries = get(legendobj, 'String');
if length(legentries) > 0
legstr = ['\legend{%' ENDL TAB ];
for i = 1:length(legentries)
legstr = [ legstr legentries{i} '\\%' ENDL ];
if i ~= length(legentries)
legstr = [ legstr TAB ];
end
end
legstr = [ legstr '}%' ENDL ];
fwrite( FID, legstr );
end
end

xpointformat = '%f';
ypointformat = '%f';
if xislog
xpointformat = '%e';
end
if yislog
ypointformat = '%e';
end

for i = 1:length(lineobjs)

x = get(lineobjs(i), 'XData');
y = get(lineobjs(i), 'YData');
z = get(lineobjs(i), 'ZData');

if size(x,1) > 1
disp( ['line element ' num2str(i) ' skipped: size ' num2str(size(x)) ' not supported']);
end
if abs(max(z) > 0)
disp( ['line element ' num2str(i) ' skipped: only 2d-plots supported up to now']);
end

if size(x,2) > parser.Results.maxpoints
% we need to re-interpolate the data!
q = find( diff(x) < 0 );
if length(q)
% parametric plot x(t), y(t), z(t).
% we assume t = 1:size(x,2)
X = 1:parser.Results.maxpoints;
x = interp1( 1:size(x,2),x, X);
y = interp1( 1:size(y,2),y, X);
z = interp1( 1:size(z,2),z, X);

else
% a normal plot y(x):
X = linspace( min(x), max(x), parser.Results.maxpoints );
y = interp1( x,y, X );
x = X;
end
end

coordstr = [];
for j = 1:size(x,2)
coordstr = [coordstr sprintf(['\t(' xpointformat ',\t' ypointformat ')\n'], x(j), y(j)) ];
end

addplotoptstr = [];
if parser.Results.styles
markOpts = {};
mark = [];
linestyle = [];
color = [];

C = matlabColorToPGFColor( get(lineobjs(i), 'Color') );
if length(C)
color = [ 'color=' C ];
end

L = get(lineobjs(i), 'LineStyle' );
switch L
case 'none'
linestyle = 'only marks';
case '-'
linestyle = [];
case ':'
linestyle = 'densely dotted';
case '-:'
linestyle = 'dash pattern={on 2pt off 3pt on 1pt off 3pt}';
case '--'
linestyle = 'densely dashed';
end

M = get(lineobjs(i), 'Marker');
switch M
case '.'
mark = '*';
markOpts = [ markOpts 'scale=0.1' ];
case 'o'
mark = '*';
case 'x'
mark = 'x';
case '+'
mark = '+';
case '*'
mark = 'asterisk';
case 'square'
mark = 'square*';
case 'diamond'
mark = 'diamond*';
case '^'
mark = 'triangle*';
case 'v'
mark = 'triangle*';
markOpts = [ markOpts 'rotate=180' ];
case '<'
mark = 'triangle*';
markOpts = [ markOpts 'rotate=90' ];
case '>'
mark = 'triangle*';
markOpts = [ markOpts 'rotate=270' ];
case 'pentagramm'
mark = 'pentagon*';
case 'hexagram'
mark = 'oplus*';
end

M = matlabColorToPGFColor( get(lineobjs(i), 'MarkerFaceColor') );
if length(M)
markOpts = [ markOpts ['fill=' M] ];
end

M = matlabColorToPGFColor( get(lineobjs(i), 'MarkerEdgeColor') );
if length(M)
markOpts = [ markOpts ['draw=' M] ];
end

if length(color)
if length(addplotoptstr)
addplotoptstr = [addplotoptstr ',' ];
end
addplotoptstr = [ addplotoptstr color ];
end

if length(linestyle)
if length(addplotoptstr)
addplotoptstr = [addplotoptstr ',' ];
end
addplotoptstr = [ addplotoptstr linestyle ];
end

if length(mark)
if length(addplotoptstr)
addplotoptstr = [addplotoptstr ',' ];
end
addplotoptstr = [ addplotoptstr [ 'mark=' mark ] ];

if length(markOpts)
markOptsStr = 'mark options={';
for q = 1:length(markOpts)
if q > 1
markOptsStr = [markOptsStr ',' ];
end
markOptsStr = [ markOptsStr markOpts{q} ];
end
markOptsStr = [ markOptsStr '}' ];

addplotoptstr = [ addplotoptstr ',' markOptsStr ];
end
end

if length(addplotoptstr)
addplotoptstr = [ '[' addplotoptstr ']' ];
end

end
fwrite( FID, [ ...
'\addplot' addplotoptstr ' plot coordinates {' ENDL coordstr '};' ENDL ] );

end

fwrite( FID, [ ...
'\end{axis}' ENDL ...
'\end{tikzpicture}%' ENDL ] );
fclose(FID);

end

function cstr = matlabColorToPGFColor( C )

if length(C) ~= 3 | ischar(C) & strcmp(C,'none'), cstr = [];
elseif norm( C - [0 0 1 ], 'inf' ) < 1e-10, cstr = 'blue';
elseif norm( C - [0 1 0 ], 'inf' ) < 1e-10, cstr = 'green';
elseif norm( C - [1 0 0 ], 'inf' ) < 1e-10, cstr = 'red';
elseif norm( C - [0 1 1 ], 'inf' ) < 1e-10, cstr = 'cyan';
elseif norm( C - [1 0 1 ], 'inf' ) < 1e-10, cstr = 'magenta';
elseif norm( C - [1 1 0 ], 'inf' ) < 1e-10, cstr = 'yellow';
elseif norm( C - [0 0 0 ], 'inf' ) < 1e-10, cstr = 'black';
elseif norm( C - [1 1 1 ], 'inf' ) < 1e-10, cstr = 'white';
else
cstr= 'blue'; % FIXME
% cstr = [ '{rgb:red,' num2str( floor( C(1)*100) ) ';green,' num2str(floor(C(2)*100)) ';blue,' num2str(floor(C(3)*100)) '}' ];
end

end