%% Copyright (C) 2008 Simone Zuccher
%%
%% This file is NOT part of Octave but you can redistribute it and/or modify it
%% under the terms of the GNU General Public License as published by
%% the Free Software Foundation. For more details
%% see the GNU General Public License at
%% <http://www.gnu.org/licenses/>.
%%
%% This is distributed in the hope that it will be useful, but
%% WITHOUT ANY WARRANTY. 

%% -*- texinfo -*-
%% @deftypefn {Function File} {} gps
%% @deftypefnx {Function File} {} gps (@var{cmd})
%% @deftypefnx {Function File} {} gps (@var{ps-type},@var{name})
%% @deftypefnx {Function File} {} gps (@var{action}, @var{data}, @var{attribute}, @dots{})
%% Motivation/purpose.
%% Octave is a great environment to run computations but it might look not 
%% very flexible to people that use Gnuplot (http://gnuplot.sourceforge.net/)
%% for making highly-customized
%% figures, i.e. figures with special labels, arrows, mathematical 
%% formulae in the legend or on the axis, and so on.
%% A simple example is, from version 3.0.0 of Octave, the impossibility 
%% to make plots with bullets (or filled circles), available in previous versions
%% of Octave.
%%
%% Description.
%% 'gps' (GnuPlot Stream) 
%% produces two-dimensional and three-dimensional plots using a direct
%% gnuplot stream of commands sent to an X11 window. 
%% Therefore, all gnuplot features are usable.
%% The only drawback is that data passed to gps are written on local (hidden)
%% files. This is necessary to allow the full usage of 'replot'.
%% 
%% Many different combinations of arguments are possible.
%%
%% @noindent
%% No arguments.
%% @example
%% gps
%% @end example
%% This causes gps to close previously opened plots, i.e.
%% if no windows were open, nothing is done; if a gps window was open, then it 
%% is closed. Usage:
%%
%%
%%
%% @noindent
%% One arument. 
%% @example
%% gps (@var{cmd})
%% @end example
%% Possibilities for @var{cmd} are:
%% @itemize @bullet
%% @item
%% A single gnuplot command or multiple gnuplot commands separated by 
%% semicolon. Examples include
%% @example
%% gps("plot [-2*pi:2*pi][-1:1] sin(x); set title 'My first plot'")
%% gps("set xlabel 'x'");
%% gps("replot cos(x); set title 'sin and cosine of x'")
%% @end example
%%
%% As you can see, make sure you use correctly " and '.
%% Note that gps adds "replot" to every gnuplot command to make sure
%% that all listed commands are executed.
%%
%% @item
%% If @var{cmd} is "exit" or "e" or "quit" or "q", gps closes the gnuplot
%% stream, i.e. this is equivalent to invoking gps without arguments. 
%% All following examples close the gnuplot stream.
%% @example
%% gps
%% gps("exit")
%% gps("e")
%% gps("quit")
%% gps("q")
%% @end example
%%
%% @item
%% If @var{cmd} is "ps" or "pss" or "psbk" or "psbk3d" a postscript figure 
%% named gps.ps is created from the current plot.
%% Command "ps" refers to a figure with the standard gnuplot settings, whereas
%% command "pss" refers to square axis (i.e. x and y axis have the same length).
%% Options with "bk" ("psbk" and "psbk3d") refer to the standard size(s)
%% used in the book by Squassina and Zuccher (2008).
%% Notice that "ps" and "pss" generate postscript 'cropped' figures, i.e. 
%% dvips tries to minimize the margins. 
%% Therefore, figures might have different BoundingBox.
%% When "psbk" or "psbk3d" are invoked, the BoundingBox is replaced
%% so as to conform to the standard in Squassina and Zuccher (2008).
%% Valid calls are
%% @example
%% gps("ps")     % -> standard gnuplot settings
%% gps("pss")    % -> square aspect ratio (2D plots)
%% gps("psbk")   % -> square aspect ratio scaled by 86%  (2D plots)
%% gps("psbk3d") % -> square aspect ratio scaled by 97%  (3D plots)
%% @end example
%% @end itemize
%%
%% @noindent
%% Two arguments. 
%% @example
%% gps (@var{ps-type},@var{root-name})
%% @end example
%% The first argument is the type of postscript plot ("ps" or
%% "pss" or "psbk" or "psbk3d", as explained above), the second
%% one is the root of figure filename without extension (the extension ".ps"
%% is added automatically by gps). Valid calls are:
%% @example
%% gps("ps","fig1")     % -> fig1.ps, standard gnuplot settings, cropped
%% gps("pss","fig2")    % -> fig2.ps, square aspect ratio, cropped
%% gps("psbk","fig3")   % -> fig3.ps, square, scaled by 86%, fix size
%% gps("psbk3d","fig4") % -> fig4.ps, square, scaled by 97%, fix size
%% @end example
%%
%% @noindent
%% Three arguments - postcript with user settings.
%% @example
%% gps("psp", @var{root-name}, @var{plot settings})
%% @end example
%% The first argument is always "psp", the second
%% one is the root of figure filename without extension (the extension ".ps"
%% is added automatically by gps). Third argument is pslatex 
%% gnuplot terminal settings. Valid calls are:
%% @example
%% gps("psp","fig5","size 14cm,5cm 6")   
%% @end example
%% creates fig5.ps, standard gnuplot settings, width of the image 
%% is 14 cm, height 5 cm, fontsize 6.
%% @example
%% gps("psp","fig6","oldstyle")   
%% @end example
%% creates fig6.ps, oldstyle setting of gnuplot pslatex terminal.
%% Gnuplot's pslatex terminal gives following possibilities:
%% (rotate | norotate) (oldstyle | newstyle) (auxfile | noauxfile)
%% (level1 | leveldefault) (color | colour | monochrome)
%% (solid | dashed) (dashlength | dl <DL>) (linewidth | lw <LW>)
%% (rounded | butt) (clip | noclip)
%% (palfuncparam <samples>(,<maxdeviation>))
%% (size <XX>(unit),<YY>(unit)) (<font_size>)
%%
%% @noindent
%% More than two aruments (main purpose of gps). 
%% @example
%% gps (@var{action}, @var{data}, @var{attribute}, @var{data}, @var{attribute}, @var{data}, @var{attribute}, @dots{})
%% @end example
%% The first argument @var{action} is the plotting mode, i.e. 
%% @example
%% "plot" or "p" for 2D plots 
%% "splot" or "s" for 3D plots without surfaces
%% "splots" or "ss" for 3D plots with surfaces
%% "replot" or "rep" or "r" for any type of plot
%% @end example
%% The second argument @var{data} is the data to be plotted and, 
%% depending on @var{action}, @var{data} can be
%% @example
%% (a) @var{x}, @var{y}, @var{z} for "splots" or "ss" where @var{x} and @var{y} are vectors of 
%%     aritrary lengths (Nx for @var{x} and Ny for @var{x}) and @var{z} is a matrix 
%%     Nx by Ny which contains the values of z(i,j) at point of 
%%     coordinates x(i),y(j).
%% (b) a single matrix @var{data} N x M for all other values of @var{action},
%%     where N is the number of points to be plotted and M is the number
%%     of columns. Gnuplot uses by default the first two columns in 2D 
%%     plots and the first three columns in 3D plots.
%% @end example
%% The third argument @var{attribute} is the data attribute and 
%% includes title (which will appear in the legend), style (with lines or 
%% with points) or whatever option, exactly as in Gnuplot.
%% If more than 3 arguments are supplied, then their number must odd because
%% the 4th argument will be data again, the 5th will be their attributes, and so
%% forth. 
%% Valid calls are
%% @example
%% gps("plot",[x y],"t '1st line' w lp",\
%%      [x y.^2],"t '2nd line' w p",\
%%      [x -abs(y)],"t '3rd line' w l;\
%%      set auto;\
%%      set xlabel 'x';\
%%      set ylabel 'y'")
%% gps("replot",[x -y.^2],"t '2nd line' w lp")
%% gps("splot",[x y sin(x).*cos(y)],"t '3D line 1' w l;\
%%      set auto",\
%%      [y x x.*y],"t '3D line 2' w lp")
%% gps("splots",x,y,z,"w l t '';\
%%      set surface;\
%%      set hidden3d;\
%%      set ticslevel 0;\
%%      set xlabel 'x';\
%%      set ylabel 'y';\
%%      set label 'z= sin(sqrt(x^2+y^2))' at  0,0,1.1;\
%%      ")
%% @end example
%% As seen in these examples, the <<last>> attribute can include commands 
%% which can influence the whole plot such as
%% axis labels, axis ranges, plot title, arrows, labels, and so on.
%% This works as long as such commands are included
%% in the <<last>> attribute and are separated by a semicolon ";".
%% @end deftypefn

%% Purpose:    To avoid using Octave for making professional figures
%% Author:     Simone Zuccher
%% Created:    26 Oct 2008
%% Bug report: zuccher@sci.univr.it

function gps(varargin)
global __gp_var



% Quit if called without arguments
if(nargin==0)
   __gp_end_in__
   return
endif


% if a gnuplot stream is not open yet, open one
__gp_start_in__


if(nargin==1)
   cmd=varargin{1};
   switch (cmd)
      case {"exit", "quit", "q", "e"}
         __gp_end_in__
	 return
      case {"ps", "pss", "psbk"}
         __gp_2ps_in__(cmd,"gps")
	 return
      case {"psp"}
         error('gps: psp is missing plot parameters');
   endswitch
   % at this point it must be a gnuplot command...
   fprintf (__gp_var.FID, "%s;replot\n",cmd);
   fflush(__gp_var.FID);
   return
endif

% if the first argument is "ps", export plot as postscript figure 
%    and the second argument is interpreted the figure name -> name.ps
if (nargin==2)
   cmd=varargin{1};
   name=varargin{2};
   switch (cmd)
      case {"ps", "pss", "psbk", "psbk3d"}
         __gp_2ps_in__(cmd,name)
	 return
      case {"psp"}
         error('gps: psp is missing plot parameters');
   endswitch
endif

if ((nargin>2) && (mod(nargin-1,2)==0))
   pltoption=varargin{1};
   % this switch is needed only to decide about deleting previous files
   switch (pltoption)
      case {"plot", "p", "splot", "s", "splots", "ss"}
         % previous plotted files are not needed and, thus, are deleted
	 system("rm -f .__gp_plt_tmpfile*.dat");
         % set number of plotted curves to 0 
	 __gp_var.NC=0;
   endswitch
   % this is the main switch
   switch (pltoption)
      case {"psp"}
         __gp_2ps_in__("psp",varargin{2:3})
      case {"plot", "p"}
	 __gp_plt_in__("plot",varargin{2:nargin})
      case {"splot", "s"}
	 __gp_plt_in__("splot",varargin{2:nargin})
      case {"splots", "ss"}
	 __gp_plt_in__("splots",varargin{2:nargin})
      case {"replot", "rep", "r"}
	 __gp_plt_in__("replot",varargin{2:nargin})
   endswitch
   return
endif

% If none of the above cases apply, then there is something wrong
disp('gps: unknown command or syntax error');
endfunction % gps



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function __gp_plt_in__(varargin)
global __gp_var

% find out the plot option and number of curves
gpoption = pltoption = varargin{1};
Ncurves = floor((nargin-1)/2);

% in case of "splots" (which does not exist in gnuplot, do differently
if(strcmp(pltoption,"splots"))
    gpoption = "splot";
    Ncurves = floor((nargin-1)/4);
endif

% final command to be sent to the stream
finalcmd=[gpoption " "];


% loop on number of curves
for i=1:Ncurves
   __gp_var.NC = __gp_var.NC+1;


   % generate a temporary file (hidden) name
   tmpname=[".__gp_plt_tmpfile" num2str(__gp_var.NC) ".dat"];
   
   % enter here only for surface plot, i.e. "splots"
   if(strcmp(pltoption,"splots")) 
      % get the data from inputs
      x = varargin{(i-1)*4+2};
      y = varargin{(i-1)*4+3};
      z = varargin{(i-1)*4+4};
      % get the lengths
      nx=length(x);
      ny=length(y);
      % open the temporary file and write data in right format for surface plot
      tmpfid=fopen(tmpname,"w");
      for ix=1:nx
	 for iy=1:ny
	    fprintf(tmpfid,"%f %f %f \n",x(ix),y(iy),z(ix,iy));
	 end
	 fputs(tmpfid,"\n");
      end
      % close data file
      fclose(tmpfid);
      % extract the attributes of the current data
      gpcmd = varargin{(i-1)*4+5};
      
      
   % enter here for regular gnuplot plots
   else 
      data = varargin{i*2};
      % if only one point is plotted, it is made a vector of 2 equal points
      if(size(data)(1)==1)
         data = [data;data];
      endif
      % save data to the file
      save("-ascii",tmpname,"data");
      % extract the attributes of the current data
      gpcmd = varargin{i*2+1};
   endif

   % add this plot to the final command
   if(i>1)
      finalcmd = [finalcmd ','];
   endif
   finalcmd = [finalcmd ' ''' tmpname ''' ' gpcmd];
%   sprintf("%s '%s' %s;",finalcmd,tmpname,gpcmd);
endfor
% wait 1/10 of a second to make sure files are written...
pause(.1)
% (re)plot data and add replot to ensure interpretation of additional commands
fprintf (__gp_var.FID, "%s; replot \n",finalcmd);
fflush(__gp_var.FID);
endfunction
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function __gp_start_in__
global __gp_var
% if __gp_var does not exists create an empty one
if (isempty(__gp_var))
   __gp_var.FID=0;
   __gp_var.ON=false;
   __gp_var.NC=0;
endif

% If no gnuplot stream is open, open one and set the global variables 
if ((__gp_var.FID == 0) && (__gp_var.ON == false) && (__gp_var.NC == 0))
   __gp_var.FID=popen("gnuplot -noraise","w");
   __gp_var.ON=true;
   __gp_var.NC=0;
   fputs(__gp_var.FID,"set term x11\n");
   fputs(__gp_var.FID,"set yrange restore; plot 1./0. t '' \n");
%else
%   disp("La variabile esiste e non ho aperto niente\n")
endif
endfunction % __gp_start_in__
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function __gp_end_in__
global __gp_var
% If __gp_var exists, is not empty and is not initialized to zero, close stream
if ((exist("__gp_var") == 1) && (!isempty(__gp_var)) && ...
       (__gp_var.ON) && (__gp_var.FID > 0))
%   disp('quitting...');
   pclose(__gp_var.FID);
   system("rm -f .__gp_plt_tmpfile*.dat");
   system("rm -f __gp_2ps__tmpxxxfig*");
   __gp_var.FID=0;
   __gp_var.ON=false;
   __gp_var.NC=0;
   clear __gp_var
endif
endfunction % __gp_end_in__
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%





%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function __gp_2ps_in__(type,name,parameters="")
global __gp_var


% Save current plot settings
fputs(__gp_var.FID,"save '.__gp_plt_tmpfileCURRENTPLOT'\n");
fflush(__gp_var.FID);
pause(.2);
% say why we are waiting
fdisp (stdout, ['generating figure ' name '.ps...']);
fflush(stdout);


% defaul option is regular gnuplot size
sizeoption = "noratio  1,1 ";
BBoption = "18 546 398 796";
fontoption = "12";
switch (type)
      case {"pss"}
         sizeoption = "square"; 
	 BBoption = "66 535 349 806";
      case {"psbk"}
         sizeoption = "square 0.86,0.86";
%         sizeoption = "square 0.9,0.9";
         BBoption = "51 567 290 809";
      case {"psbk3d"}
         fontoption = "8";
         sizeoption = "square 0.97, 0.97";
%         BBoption = "70 550 298 743";
         BBoption = "70 560 298 753";
	 fputs(__gp_var.FID,"set format x '%g \\vspace{-3mm}\\hspace{1mm}'\n");
         fputs(__gp_var.FID,"set format y '%g \\vspace{0mm}\\hspace{-5mm}'\n");
%         fputs(__gp_var.FID,"set format z '%g \\vspace{1mm}\\hspace{-4mm}'\n");
         fflush(__gp_var.FID);
endswitch

switch (type)
      case {"psp"}
         fputs(__gp_var.FID,["set term pslatex " parameters "\n"]);
         fputs(__gp_var.FID,"set output '__gp_2ps__tmpxxxfig.tex'\n");
         fputs(__gp_var.FID,"replot\n");
         fflush(__gp_var.FID);
      otherwise
         %for i=1:2
         % Export from gnuplot to pslatex
         fputs(__gp_var.FID,["set term pslatex " fontoption "\n"]);
         fputs(__gp_var.FID,"set output '__gp_2ps__tmpxxxfig.tex'\n");
         fputs(__gp_var.FID,["set size " sizeoption "\n"]);
         %fputs(__gp_var.FID,"set key spacing 1.7\n");
         fputs(__gp_var.FID,"replot\n");
         fflush(__gp_var.FID);
endswitch

% Reset things as before
fputs(__gp_var.FID,"set term x11\n");
pause(.1);
fputs(__gp_var.FID,"load '.__gp_plt_tmpfileCURRENTPLOT'\n");
fputs(__gp_var.FID,"replot\n");
fflush(__gp_var.FID);
%end

% Remove temporary file
system("rm -f .__gp_plt_tmpfileCURRENTPLOT");

% Create a temporary LaTeX file to include the figure generated above
system("echo ' \\\\documentclass[12pt,a4paper]{article}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\setlength{\\\\topmargin}{-40 mm}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\setlength{\\\\oddsidemargin}{-25 mm}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\setlength{\\\\textwidth}{200 mm}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\setlength{\\\\textheight}{200 mm}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\setlength{\\\\footskip}{0 mm}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\usepackage{graphics}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\usepackage{amsmath}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\usepackage{textcomp}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\pagestyle{empty}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\begin{document}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\input{__gp_2ps__tmpxxxfig.tex}' >> __gp_2ps__tmpxxxfiglatexfile.tex");
system("echo ' \\\\end{document}' >> __gp_2ps__tmpxxxfiglatexfile.tex");

% Run LaTeX without showing anything on the screen
system("latex __gp_2ps__tmpxxxfiglatexfile.tex > /dev/null");

% Make the postscript file in silent mode
system("dvips -E -o __gp_2ps__tmpxxxfiglatexfile.ps __gp_2ps__tmpxxxfiglatexfile -q");

% Replace bounding box only for psbk and psbk3d
switch (type)
   case {"psbk", "psbk3d"}
      system(["sed s/'BoundingBox: '/'BoundingBox: " BBoption " % '/ \
        < __gp_2ps__tmpxxxfiglatexfile.ps > __gp_2ps__tmpxxxfiglatexfilenew.ps"]);
      system("cp __gp_2ps__tmpxxxfiglatexfilenew.ps __gp_2ps__tmpxxxfiglatexfile.ps");
endswitch

% Change the title of the figure to the correct one and finish
systemcm = ["sed s/'__gp_2ps__tmpxxxfiglatexfile.dvi'/'" name ".dvi'/ < \
        __gp_2ps__tmpxxxfiglatexfile.ps > " name ".ps"];
system(systemcm);

% Clear up temporary files
system("rm -f __gp_2ps__tmpxxxfiglatexfile* __gp_2ps__tmpxxxfig.tex");

% Done
endfunction % __gp_2ps_in__
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
