# The `plot', `splot', `replot', and `unplot' functions constitute
# Algae's interface to the gnuplot plotting package.  Use `plot' for
# plotting lines in two or three dimensions; `splot' for plotting
# surfaces.  Two and three dimensional plots may not be mixed in a
# single plot window.

# `plot' and `splot' take up to 3 arguments.  If either or both of
# the first two arguments are (or can be converted to) character
# vectors, then their elements are sent, each on a separate line, to
# gnuplot as commands.  For example, `plot("set term X11")' causes
# gnuplot to generate X11 output.  If all 3 arguments are NULL the
# open and active plot windows are printed and the active window
# number is returned.

# If more than one argument is given and the last one is an integer
# scalar, then it is taken to be the identifier of the plot window.
# If not specified then window 1 is used by default.  Entering a
# different number will open another plot window.  If the specified
# window is already open it will be overwritten.

# If either or both of the first two arguments to `plot' or `splot'
# are not integer or character scalars, they are taken to be data for
# plotting.  If a table is given, then the members are plotted each
# with a different line and point type.  If any member is itself a
# table, then the curves described by its members are plotted with
# the same line and point type.

# If `plot' is given a vector or a matrix with 2 columns, a two
# dimensional line is plotted (vector elements vs. labels; column 2
# vs. column 1).  If `plot' is given a matrix with 3 columns, the set
# of triplets is taken to be x,y,z data points and a line is plotted
# in 3 dimensions.

# The input to `splot' is a matrix of z values.  Corresponding values
# of x and y are taken from the matrix rids and cids, respectively.
# A surface is drawn through the matrix of points.

# If a vector (`plot') or matrix (`splot') does not have labels, then
# the row/col indices are used instead.

# `replot' takes up to 2 arguments.  If the first argument is
# character data, it is passed to gnuplot as commands (as described
# above).  The second argument (or first if no gnuplot commands are
# given) is the integer scalar plot window identifier.  If not
# given, the previous one is used.  Both arguments are optional.

# The lone argument to `unplot' is a scalar or vector containing the
# identifier(s) of the plot windows to be terminated.  If omitted, the 
# previous plot window is closed.  Optionally, the string "all" may
# be given, in which case all plot windows are closed.


plot = function( a; b; c ) {

    return plot.static.top( plot.static.plot;
                            a; b; c );
};

splot = function( a; b; c ) {

    return plot.static.top( plot.static.splot;
                            a; b; c );
};

plot.static = {};

plot.static.pvec = fill( 0; 0 );
plot.static.pvec.eid = fill( 0; 0 );
plot.static.tfile = fill(0;"");
plot.static.tfile.eid = fill(0;"");

plot.static.top = function( plotter; a; b; c ) {

    local( cmds; data; pipe; win_kill );

    if (a == NULL & b == NULL & c == NULL) {
       a = plot.static.pvec;
       return a[ reverse( isort( a.eid ) ) ];
    elseif (a == NULL)
       message("run time error: NULL passed as 1st argument.");
       exception();
    }

    cmds = "";
    data={};
    win_kill = 0;

    if (a.type == "character") {
       cmds = a;
    elseif ( class(a) == ("matrix","vector") )
       data.y_1 = a;
    elseif ( class(a) == "table" )
       data = a;
    else
       message("run time error: 1st argument invalid data type.");
       exception()
    }

    if (b != NULL) {
       if (b.type == "character") {
          if ( cmds ) {cmds += ";";}
          cmds += b;
       elseif ( class(b) == ("matrix","vector") )
          if ( data.y_1 ) {data.y_2 = b; else data.y_1 = b;}
       elseif ( class(b) == "table" )
          data += b;
       elseif (class(b) == "scalar"  &  c == NULL)
          pipe = integer(b);
       else
          message("run time error: 2nd argument invalid data type.");
          exception()
       }
       if ( c != NULL ) {pipe = integer(c);}
    else
       if ( c != NULL ) {
          message("run time error: Embedded NULL argument.");
          exception();
       }
    }

    if ( data ) {win_kill = 1;}

    pipe = plot.static.p_mgr(pipe; win_kill);

    return plotter( cmds; data; pipe );

};

plot.static.plot = function( cmds; data; pipe )
{
    # The `cmds' argument contains character strings that are sent
    # directly to gnuplot as commands.  It should be (or be
    # convertible to) a vector; each element is sent to gnuplot as a
    # separate line.

    # The `data' argument contains the data to be plotted.  If `data'
    # is a vector (or can be converted to one), then its elements
    # contain the ordinates.  If it also has numeric labels, they are
    # used for the abscissae.  If `data' is a matrix with 2 columns,
    # column 2 is plotted against column 1.  If `data' is a table,
    # each member is treated like the single vector or matrix
    # described above, and gives one curve of a multi-curve plot.

    # The `pipe' argument specifies the integer scalar plot window
    # identifier.  If it's NULL, the previous window is closed and
    # then used.  The identifier is returned by this function.

    local( tr_tmpfile; tmpfile; c; width; d; p; name; plt; t; nm );

    for ( c in vector( cmds ) ) { fprintf( pipe; "%s\n"; c ); }

    if ( data != NULL ) {

        plt = "";

        if ( members(data).ne > 0 ) {

	    p = "";
    
	    for ( name in members( data ) ) {

	      tmpfile = tmp_file();

              # Use tr(1) to remove "[" and "]" characters.  Because
              # handling of these characters varies incompatibly
              # between implementations, we have to use their ASCII
              # values.

	      tr_tmpfile = sprintf( "!%s -d '\\133\\135' > %s";
				    $programs.tr; tmpfile );

	      t = data.(name);
              if ( class( t ) != "table" ) {
		t = {};
		t.(name) = data.(name);
	      }

              for ( nm in members( t ) ) {
    
                if (class(t.(nm)) == "vector" ) {
		   d = t.(nm);
		   if ( d.eid != NULL ) {
                      d.eid.eid = NULL;
		      d = [ d.eid; d ]';
		   else
		      d = [ 1:d.ne; d ]';
		   }
                   if (!test(plt)) {
		      plt = "plot";
		   elseif (plt != "plot")
		      message("run time error: Cannot mix 2-D and 3-D plots.");
		      exception();
		   }

		elseif (class(t.(nm)) == "matrix" & t.(nm).nc == 2)
		   d = t.(nm);
                   if (!test(plt)) {
		      plt = "plot";
		   elseif (plt != "plot")
		      message("run time error: Cannot mix 2-D and 3-D plots.");
		      exception();
		   }

		elseif (class(t.(nm)) == "matrix" & t.(nm).nc == 3)
		   d = t.(nm);
                   if (!test(plt)) {
		      plt = "splot";
		   elseif (plt != "splot")
		      message("run time error: Cannot mix 2-D and 3-D plots.");
		      exception();
		   }

		else
		   message("run time error: Invalid data for plotting.");
		   exception();
		}
    
		if ( d.type == ( "complex", "character" ) ) {
		    message( "run time error: Invalid type for plotting." );
		    exception();
		}
    
    		width = $term_width;
    		$term_width = 0;

		print( dense( d ); tr_tmpfile );
		fprintf( tr_tmpfile; "\n" );

                $term_width = width;
              }

	      if ( p != "" ) { p += ", "; }
              if (plt == "plot") {
	         p += sprintf( "'%s' title '%s'";
	      	      tmpfile; name );
	      else
	         p += sprintf( "'%s' title '%s'";
                        tmpfile; name );
	      }
    	      
	      close( tr_tmpfile );
    	      
	      plot.static.tfile = plot.static.tfile, tmpfile;
	      plot.static.tfile.eid[plot.static.tfile.ne] = pipe;

	    }
    
	    if (plt == "splot") {fprintf( pipe; "set parametric\n" );}
	    fprintf( pipe; "%s %s\n"; plt; p );

        }
    }

    return plot.static.pvec[ imax( plot.static.pvec.eid ) ];

};

replot = function( cmds; pipe ) {

    # The `cmds' vector contains character strings that are sent
    # directly to gnuplot as commands.

    # The `pipe' argument specifies the integer scalar plot window
    # identifier.  If it's NULL, the previous window is closed and
    # then used.  The identifier is returned by this function.

    local( c );

    if (cmds != NULL) {
       cmds = vector (cmds);
       if (cmds.type != "character") {
          if (pipe != NULL) {
             message("run time error: gnuplot commands must be type character, not %s."; cmds.type );
             exception();
          else
             pipe = integer (scalar (cmds));
             cmds = NULL;
          }
       }
    }

    if (pipe == NULL) {
       pipe = plot.static.pvec[ imax( plot.static.pvec.eid) ];
    }

    if ( ! find( pipe; plot.static.pvec ).ne ) {
        message( "run time error: Plot %d is not open."; pipe );
        exception();
    }

    pipe = plot.static.p_mgr(pipe; 0);
    
    for ( c in vector( cmds ) ) { fprintf( pipe; "%s\n"; c ); }

    fprintf( pipe; "replot\n" );

    return plot.static.pvec[ imax( plot.static.pvec.eid ) ];

};


unplot = function(pipe)
{
    # The `pipe' argument specifies the integer scalar plot window
    # identifier(s).  If it's NULL, the previous window is closed.
    # If equal to "all", all windows are closed.

    local(i; j; plist; p; l; v);

    if ( pipe != NULL ) {
	if ( pipe[1].type == "character" ) {
	    if ( pipe == "all" ) { pipe = plot.static.pvec; }
	}
    }

    if ( plot.static.pvec.ne == 0 ) {
       message("Warning: No plot to close.");
       return fill( 0; 0 );
    }

    if (pipe == NULL) {
       pipe = plot.static.pvec[ imax( plot.static.pvec.eid ) ];
    }

#  remove specified plot window from the list as well as any
#  temporary files associated with it.

    v = integer(vector());
    plist = vector(pipe);
    for (pipe in plist) {
       i = find(pipe;plot.static.pvec);
       if ( i ) {
          v = v,pipe;
          p = sprintf("!%s # %d"; $programs.gnuplot; pipe);
          close(p);
          plot.static.pvec = plot.static.pvec[lose(i;1:plot.static.pvec.ne)];
          l = find(p;plot.static.tfile.eid);
          if ( l ) {
             for (j in l) {
                close(plot.static.tfile[j]);
                system(sprintf("/bin/rm -f %s";plot.static.tfile[j]));
             }
             plot.static.tfile = plot.static.tfile[lose(l;1:plot.static.tfile.ne)];
          }
       else
          message( "warning: Plot %d is not open."; pipe );
       }
    }
    if (v.ne == 1) {return v[1]; else return v;}

};

plot.static.splot = function( cmds; data; pipe )
{
    # The `cmds' argument contains a vector of character strings that
    # are sent directly to gnuplot as commands.  If not a vector it
    # should be convertible to a vector.  Each element in the vector is
    # sent to gnuplot as a separate line; e.g. "set data style lines",
    # "set title 'Sample'", "set nokey", etc.

    # The `data' argument contains the data to be plotted.  If `data'
    # is a matrix, the matrix values are z (elevation) values of a
    # surface.  The corresponding x and y values are taken from the
    # matrix row and column labels, respectively.  If no labels
    # exist, the row/column indices are used instead.  If `data' is a
    # table, each member is treated like the single matrix described
    # above, and gives one curve of a multi-curve surface plot.

    # The `pipe' argument specifies the integer scalar plot window
    # identifier.  If it's NULL, the previous window is closed and
    # then used.  The identifier is returned by this function.

    local( tmpfile; c; d; p; name; i; j; t; nm );

    for ( c in vector( cmds ) ) { fprintf( pipe; "%s\n"; c ); }

    if ( data != NULL ) {

        p = "";

        for ( name in members( data ) ) {

          tmpfile = tmp_file();

          t = data.(name);
	  if ( class( t ) != "table" ) {
	    t = {};
	    t.(name) = data.(name);
	  }

	  for ( nm in members( t ) ) {

            d = matrix( t.(nm) );
            if (d.rid == NULL) {d.rid = 1:d.nr;}
            if (d.cid == NULL) {d.cid = 1:d.nc;}

            if ( d.type == ( "complex", "character" ) ) {
                message( "run time error: Invalid matrix type for plotting." );
                exception();
            }

	    for (j in 1:d.nc) {
               for (i in 1:d.nr) {
                  fprintf(tmpfile;" %g   %g   %g\n";d.rid[i];d.cid[j];d[i;j]);
               }
               fprintf(tmpfile;"\n");
            }
	    fprintf( tmpfile; "\n" );

	  }

          if ( p != "" ) { p += ", "; }
          p += sprintf( "'%s'  title '%s'"; tmpfile; name );
	  
          plot.static.tfile = plot.static.tfile, tmpfile;
          plot.static.tfile.eid[plot.static.tfile.ne] = pipe;
	  
          close( tmpfile );

        }

        fprintf( pipe; "set parametric\n" );
        fprintf( pipe; "splot %s\n"; p );

    }

    return plot.static.pvec[ imax( plot.static.pvec.eid ) ];

};


plot.static.p_mgr = function(pipe; kill)
{
    # This function is used to keep track of active and inactive plot
    # windows and temporary files.

    # The `pipe' argument specifies the integer scalar plot window
    # identifier.  If it's NULL, the previous window is closed and
    # then used.  The name of the gnuplot process, which includes the
    # identifier is returned by this function.

    # the `kill' argument indicates whether a plot window will be
    # overwritten (kill=1) or added to (kill=0).

    local(i;l;p);

    if ( plot.static.pvec.ne == 0 ) {
       if (pipe == NULL) {
          plot.static.pvec = vector(1);
          plot.static.pvec.eid = vector(1);
       }
    }

    if (pipe == NULL) {
       pipe = plot.static.pvec[ imax( plot.static.pvec.eid ) ];
       if ( kill ) {close(sprintf("!%s # %d"; $programs.gnuplot; pipe));}

    elseif ( i = find(pipe;plot.static.pvec) )
       i = scalar(i);
       plot.static.pvec.eid[i] = max(plot.static.pvec.eid)+1;

    else
       plot.static.pvec = plot.static.pvec,pipe;
       plot.static.pvec.eid[plot.static.pvec.ne] = max(plot.static.pvec.eid)+1;

    }

# keep track of temporary files; delete if no longer required

    p = sprintf("!%s # %d"; $programs.gnuplot; pipe);
    l = find(p;plot.static.tfile.eid);
    if (test(l) & kill) {
       for (i in l) {
          close(plot.static.tfile[i]);
          system(sprintf("/bin/rm -f %s";plot.static.tfile[i]));
       }
       plot.static.tfile = plot.static.tfile[lose(l;1:plot.static.tfile.ne)];
    }

    return p;

};


syntax highlighted by Code2HTML, v. 0.9.1