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
