DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T g

⟦579e0adc0⟧ TextFile

    Length: 26416 (0x6730)
    Types: TextFile
    Names: »graph.cc«

Derivation

└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89
    └─⟦cc8755de2⟧ »./libg++-1.36.1.tar.Z« 
        └─⟦23757c458⟧ 
            └─⟦this⟧ »libg++/etc/graph/graph.cc« 

TextFile

// graph.cc --- graph reads data and writes out a plot file.

char *copyright_notice = "\
Copyright (C) 1989 Free Software Foundation \n\
 \n\
This file is part of GNU CC. \n\
 \n\
GNU CC is distributed in the hope that it will be useful, \n\
but WITHOUT ANY WARRANTY.  No author or distributor \n\
accepts responsibility to anyone for the consequences of using it \n\
or for whether it serves any particular purpose or works at all, \n\
unless he says so in writing.  Refer to the GNU CC General Public \n\
License for full details. \n\
 \n\
Everyone is granted permission to copy, modify and redistribute \n\
GNU CC, but only under the conditions described in the \n\
GNU CC General Public License.   A copy of this license is \n\
supposed to have been given to you along with GNU CC so you \n\
can know your rights and responsibilities.  It should be in a \n\
file named COPYING.  Among other things, the copyright notice \n\
and this notice must be preserved on all copies. \n\
";

#include <stream.h>
#include <String.h>
#include <PlotFile.h>
#include <ctype.h>
#include "point.XPlex.h"
#include <GetOpt.h>

#define sp <<" "<<
#define nl <<"\n"

// eGetOpt is a subclass of GetOpt which provides functions for
// handling arguments to the options.

class eGetOpt : public GetOpt
{
public:

  // first_char returns the first character of the argument.

  int first_char () { return nargv[optind][0];};


  //  next_arg looks at nargv[optind] for an interger, double or string
  // depending on the type of argument given to it. If the correct type is
  // found, the value is set and next_arg returns 1.  If the type is not
  // correct, next_arg returns 0.
  
  // double arguments start with a digit, plus, minus or period.
  // integer arguments start with a digit.
  // String arguments have no restriction.
  
  int next_arg (int &i)
    {
      int tmp;
      if (0 < sscanf (nargv[optind], "%d", &tmp))
	{
	  i = tmp;
	  optind++;
	  return 1;
	}
      else
	return 0;
    }

  // If the next argument is an integer, set the reference variable to it
  // and increment the index to the options.  Return 1 if an integer is
  // found, else return 0.

  int next_arg (double &d)
    {
      double tmp;
      if (0 < sscanf (nargv[optind], "%lf", &tmp))
	{
	  d = tmp;
	  optind++;
	  return 1;
	}
      else
	return 0;
    }

  int next_arg (String &s)
    {
      if ('-' != nargv[optind][0])
	{
	  s = nargv[optind];
	  optind++;
	  return 1;
	}
      else
	return 0;
    }
};


// ePlotFile is an extended plot file class which has adjusted labels.

// put the left center or right edge of the text at the current point.
typedef enum alabel_xadj
{ LEFT_JUSTIFY = 'l', CENTER_JUSTIFY = 'c', RIGHT_JUSTIFY = 'r'};

// put the top center or bottom edge of the text at the current point.
typedef enum alabel_yadj
{ BOTTOM_FLUSH = 'b', CENTER_FLUSH = 'c', TOP_FLUSH = 't' };

class ePlotFile : public PlotFile
{
public:
  ePlotFile& ePlotFile:: alabel (alabel_xadj x_adjust,
				 alabel_yadj y_adjust, char *s)
    {
      cmd ('T');
      cmd (x_adjust);
      cmd (y_adjust);
      (PlotFile*)(this)->operator <<(s);  // workaround -dl
      (PlotFile*)(this)->operator<<("\n");
      return *this;
    }
};

// data_type value indicates the type of data present in the input files.
typedef enum { ASCII, DOUBLE, INT } data_type;

				// command line option data:

String default_label;		// default label at each point.
String top_label;		// label above the plot.
String x_label;			// x axis label
String y_label;			// y axis label
data_type input_data = ASCII;	// the type of data to be read in.
double char_height = .03;	// fractional height of printed characters
double char_width = .02;	// fractional width of printed characters
double height = .8;		// fraction height of the plot.
double lower_limit = 0.;	// lower limit in x for generated values
double no_of_ticks = 5.;	// number of tick marks on the axes.
double right = .1;		// the fractional margin on the right side.
double size_of_ticks = .01;	// fractional size of the tick marks.
double spacing = 1.;		// stepsize for equally spaced generated values
double symbol_size = .01;	// index of symbol drawn at each point.
double up = .1;			// the fractional margin above the plot.
double width = .8;		// fraction width of the plot.
double x_lower_limit = HUGE;	// HUGE means get it from the data
double x_margin = 0.0;		// fractional margin between data and box
double x_upper_limit = HUGE;	// HUGE means get it from the data
double y_lower_limit = HUGE;	// HUGE means get it from the data
double y_margin = 0.05;		// fractional margin between data and box
double y_upper_limit = HUGE;	// HUGE means get it from the data
int abcissa_flag = 0;		// nonzero means generate x axiz values
int break_flag = 0;		// break the line whenever x decreases.
int debug_flag = 0;		// verbose debugging output.
int extended_plot_format = 0;	// nonzero means use adjusted labels.
int grid_style = 1;		// style of box and or axes.
int line_style = 0;		// the type of line drawn to connect points.
int no_standard_input = 0;	// nonzero means do not read from standard input
int plot_size = 4096;		// upper limit of plot file coordinates
int save_screen_flag = 0;	// nonzero means do not erase before plotting.
int switch_style = 0;		// switch line style for each new curve
int switch_symbols = 0;		// switch symbols when starting each new curve
int symbol_number = 0;		// index of symbol drawn at each point.
int transpose_axes_flag = 0;	// nonzero means interchange x and y axes.
int x_log_scale = 0;		// the x axis is log scale
int y_log_scale = 0;		// the y axis is log scale

int error_occurred = 0;		// non zero for a bad command line option
char *usage_message = "\
[options...]\n\
\n\
    Option:                         Description:\n\
    -C                              print copyright notice\n\
    -D                              binary double precision data\n\
    -E                              use extended plot file format\n\
    -H CHARACTER_HEIGHT             fractional height of characters\n\
    -I                              binary integer data\n\
    -M [x|y] MARGIN                 margin between data and edges of box\n\
    -N TICKS                        number of tick marks on each axis\n\
    -P SIZE                         plot file coordinate range\n\
    -S SYMBOL_NUMBER SYMBOL_SIZE    draw symbols at each point\n\
    -T TICK_SIZE                    fractional size of tick marks\n\
    -W CHARACTER_WIDTH              fractional width of characters\n\
    -X X_LABEL                      label printed below the x axis\n\
    -Y Y_LABEL                      label printed right of the y axis\n\
    -a STEP_SIZE LOWER_LIMIT        generate abcissa, read only y values\n\
    -b                              break lines whenever x decreases\n\
    -c POINT_LABEL                  default label printed at each point\n\
    -d                              print debugging information\n\
    -g GRID_STYLE                   draw a grid in the plot\n\
    -h HEIGHT                       fractional height of the plot\n\
    -l TOP_LABEL                    label printed above the plot\n\
    -m LINE_MODE                    solid and dashed lines\n\
    -r RIGHT                        move plot right by fractional ammount\n\
    -s                              save the screen - do not erase\n\
    -t                              transpose x ans y axes\n\
    -u UP                           move plot up by fractional ammount\n\
    -w WIDTH                        fractional width of the plot\n\
    -x l LOWER_LIMIT UPPER_LIMIT    log scale, axis limits\n\
    -y l LOWER_LIMIT UPPER_LIMIT    log scale, axis limits\n\
    -z                              do not read data from standard input\n\
";


typedef enum op {END, CONT, MOVE, CIRCLE}; // a graphic operation

struct coord			// a component coordintate within a symbol
{
  double x, y;			// fractional coordinates
  op operation;			// the type of graphic
};

struct coord symbol[][10] =	// set of symbols
{
  {				// no symbol (blank)
    {  .0,  .0, END}
  }, {				// plus sign
    { -.5,  .0, MOVE},
    {  .5,  .0, CONT},
    {  .0, -.5, MOVE},
    {  .0,  .5, CONT},
    {  .0,  .0, END}
  }, {				// cross
    { -.5, -.5, MOVE},
    {  .5,  .5, CONT},
    {  .5, -.5, MOVE},
    { -.5,  .5, CONT},
    {  .0,  .0, END}
  }, {				// diamond
    { -.5,  .0, MOVE},
    {  .0,  .5, CONT},
    {  .5,  .0, CONT},
    {  .0, -.5, CONT},
    { -.5,  .0, CONT},
    {  .0,  .0, END}
  }, {				// square
    { -.5, -.5, MOVE},
    { -.5,  .5, CONT},
    {  .5,  .5, CONT},
    {  .5, -.5, CONT},
    { -.5, -.5, CONT},
    {  .0,  .0, END}
  }, {				// triangle
    { -.5, -.5, MOVE},
    {  .0,  .86603, CONT},
    {  .5, -.5, CONT},
    { -.5, -.5, CONT},
    {  .0,  .0, END}
  }, {				// circle
    {  .5,  .0, CIRCLE},
    {  .0,  .0, END}
  }, {				// add more symbols here...
    {  .0,  .0, END}
  }
};

// The resolution of the output device is much less than seven digits.
// A_HAIR_MORE is used to ingore the effects of round off error which should
// occur in the last few of the 16 digits.
#define A_HAIR_MORE 1.0000001

double
tick_interval (double no_of_ticks, double &lower_limit, double &upper_limit)
{
  if (lower_limit == upper_limit)
    {				// make sure the range is nonzero.
      if (lower_limit == 0.)
	{
	  lower_limit = -1.;	// this is the tradtional behavior of graph.
	  upper_limit =  1.;
	}
      else
	{
	  lower_limit *= .9;
	  upper_limit *= 1.1;
	}
    }
				// compute interval for tick marks.
  double exp = 1.;
  int i = (int) floor (log10 (fabs (upper_limit - lower_limit)) * A_HAIR_MORE);
  for (; 0 < i; i--) exp *= 10.;
  for (; 0 > i; i++) exp /= 10.;
  double mant = (upper_limit - lower_limit) / exp;

  double interval = 10.;
  double stepsize = 1.;
  while (interval * (no_of_ticks - 1.) > fabs (mant) * A_HAIR_MORE)
    {
      if (interval - stepsize <= 0.) stepsize /= 10.;
      interval -= stepsize;
    }
  interval *= exp;
  if (mant < 0.) interval = - interval;
  return interval;
}

/* read_data reads ascii (binary) data from an istream (File) of
coordinates and labels.  It passes back the array of points (x, y, and
label) containing the data. */

overload read_data;
void
read_data (istream& in, pointXPlex &pplex,
	   int auto_abscissa, double x_start,
	   double delta_x, int &symbol_number)
{
  char next_char;		// next character to be read
  point p;			// the point we are currently reading in.
  double prev_x;		// the previous x value.
  
  if (auto_abscissa)
    x_start -= delta_x;
  
  if (input_data == ASCII)
    {				// input contains ascii data
      while (in.good ())
	{
	  if (auto_abscissa)
	    p.x = x_start = x_start + delta_x;
	  else
	    in >> p.x >> WS;
	  if (switch_symbols && (p.x < prev_x))
	    symbol_number++;
	  prev_x = p.x;
	  
	  in >> p.y;
	  if (!in.good ())
	    {
	      char *line;
	      in.clear ();
	      in.gets (&line);
	      // if the input contains one coordinate per line
	      // you win here --- emacs can find the source
	      // line from this error message.
	      cerr << in.name () << ":" << 2 + pplex.high()
		<< ": unable to read the" sp 2 + pplex.high()
		  << "th y coordiate.\n";
	      cerr << in.name () << ":" << 2 + pplex.high()
		<< ": unexpected `" << line << "'\n";
	      break;
	    }
	  in >> WS;
	  p.label = (char *) 0;	// by default there is no label
	  in.get (next_char);	// look ahead for a label
	  if (in.good ())
	    {
	      in.unget (next_char);
	      if (!isdigit (next_char)	// if a lable is found
		  && (next_char != EOF)
		  && (next_char != '.')
		  && (next_char != '+')
		  && (next_char != '-'))
		{
		  in.gets (&p.label);	// store it with x and y
		}
	    }
	  p.symbol = symbol_number;
	  pplex.add_high (p);
	}
    }
  return;
}

void
read_data (File& in, pointXPlex &pplex,
	   int auto_abscissa, double x_start,
	   double delta_x, int &symbol_number)
{
  point p;			// the point we are currently reading in.
  p.label = (char *) 0;		// binary files contain no labels.
  double prev_x;		// the previous x value.
  
  if (auto_abscissa)
    x_start -= delta_x;
  
  if (input_data == DOUBLE)
    {				// input contains binary double precision
      while (in.good ())
	{
	  if (auto_abscissa)
	    p.x = x_start = x_start + delta_x;
	  else
	    {
	      in.read (&p.x, sizeof(p.x), 1);
	      if (switch_symbols && (p.x < prev_x))
		symbol_number++;
	      prev_x = p.x;
	    }
	  in.read (&p.y, sizeof(p.y), 1);
	  p.symbol = symbol_number;
	  if (in.good ())
	    pplex.add_high (p);
	}
    }
  if (input_data == INT)
    {				// input contains binary integers
      int i;
      while (in.good ())
	{
	  if (auto_abscissa)
	    p.x = x_start = x_start + delta_x;
	  else
	    {
	      in.read (&i, sizeof(i), 1);
	      p.x = i;
	      if (switch_symbols && (p.x < prev_x))
		symbol_number++;
	      prev_x = p.x;
	    }
	  in.read (&p.y, sizeof(p.y), 1);
	  p.symbol = symbol_number;
	  if (in.good ())
	    pplex.add_high (p);
	}
    }
  return;
}


// the names understood in the plot file
char *line_style_name[] =
{
  "solid",
  "longdashed",
  "dotted",
  "disconnected",
  "dotdashed",
  "shortdashed"
  };


inline int px (double t)	// transform fractional x to plot x
{
  register int x = (int) (plot_size * (width * t + right));
  if (x > plot_size)
    return plot_size;
  else if (x < 0)
    return 0;
  else
    return x;
}

inline int py (double t)	// transorm fractional x to plot x
{
  register int y = (int) (plot_size * (height * t + up));
  if (y > plot_size)
    return plot_size;
  else if (y < 0)
    return 0;
  else
    return y;
}

// uppper and lower bounds on the data
double xmin = HUGE, ymin = HUGE, xmax = -HUGE, ymax = -HUGE;
double log_xmin, log_ymin, log_xmax, log_ymax;

inline double fx (double t) // transform data x to fractional x
{
  if (x_log_scale)		// we should store these
    return (log (t) - log (xmin)) / (log (xmax) - log (xmin));
  else
    return (t - xmin) / (xmax - xmin);
}

inline double fy (double t) // transform data y to fractional y
{
  if (y_log_scale)		// we should store these
    return (log (t) - log (ymin)) / (log (ymax) - log (ymin));
  else
    return (t - ymin) / (ymax - ymin);
}

inline int in_box (double x, double y) // return 1 if point is inside box
{
  return (x >= xmin) && (x <= xmax) && (y >= ymin) && (y <= ymax);
}



int
main (int argc, char **argv)
{
  eGetOpt getopt
    (argc, argv,
     "CDEH::IN::P::ST::W::X::Y::a::bc::dg::h::l::m::r::stu::vw::x::y::");
  int option_char;
  
  while (EOF != (option_char = getopt ()))
    switch (option_char)
      {
      case 'C':
	cerr << copyright_notice; break;
      case 'D':	input_data = DOUBLE; break;
      case 'E':	extended_plot_format++; break;
      case 'H': getopt.next_arg (char_height); break;
      case 'I':	input_data = INT; break;
      case 'M':
	if ('x' == getopt.first_char())
	  {getopt.optind++; getopt.next_arg (x_margin);} break;
	if ('y' == getopt.first_char())
	  {getopt.optind++; getopt.next_arg (y_margin);} break;
      case 'N':	getopt.next_arg (no_of_ticks); break;
      case 'P':	getopt.next_arg (plot_size); break;
      case 'S':	getopt.next_arg (symbol_number); 
	getopt.next_arg (symbol_size); break;
      case 'T': getopt.next_arg (size_of_ticks); break;
      case 'W': getopt.next_arg (char_width); break;
      case 'X': getopt.next_arg (x_label); break;
      case 'Y': getopt.next_arg (y_label); break;
      case 'a':	abcissa_flag++; getopt.next_arg (spacing);
	getopt.next_arg (lower_limit); break;
      case 'b':	break_flag++; break;
      case 'c':	getopt.next_arg (default_label); break;
      case 'd':	debug_flag++; break;
      case 'g': getopt.next_arg (grid_style); break;
      case 'h': getopt.next_arg (height); break;
      case 'l': getopt.next_arg (top_label); break;
      case 'm': getopt.next_arg (line_style); break;
      case 'r': getopt.next_arg (right); break;
      case 's': save_screen_flag++; break;
      case 't': transpose_axes_flag++; break;
      case 'u': getopt.next_arg (up); break;
      case 'v': cerr << "graph version 0.\n"; break;
      case 'w': getopt.next_arg (width); break;
      case 'x':
	if ('l' == getopt.first_char()) {x_log_scale++; getopt.optind++;}
	getopt.next_arg (x_lower_limit); getopt.next_arg (x_upper_limit); break;
      case 'y':
	if ('l' == getopt.first_char()) {y_log_scale++; getopt.optind++;}
	getopt.next_arg (y_lower_limit); getopt.next_arg (y_upper_limit); break;
      case 'z': no_standard_input++; break;
      case '?': error_occurred++;
      }
  if (error_occurred)
    cerr << "usage" sp argv[0] sp usage_message;
  
  pointXPlex point;		// all the data is held in point
  
  // read data from standard input.
  File input_file;
  if (! no_standard_input)
    {
      if (input_data == ASCII)
	{			// ascii data
	  read_data (cin, point, abcissa_flag,
		     lower_limit, spacing, symbol_number);
	}
      else
	{			// binary data
	  input_file.open (stdin);
	  read_data (input_file, point, abcissa_flag,
		     lower_limit, spacing, symbol_number);
	}
    }
  
  // read data from files specified on command line.
  int i;
  for (i=getopt.optind; i<getopt.nargc; i++)
    {
      if (input_data == ASCII)
	{			// ascii data
	  cin.open (getopt.nargv[i], io_readonly, a_useonly);
	  if (cin.readable ())
	    {
	      read_data (cin, point, abcissa_flag,
			 lower_limit, spacing, symbol_number);
	    }
	}
      else
	{			// binary data
	  input_file.open (getopt.nargv[i], io_readonly, a_useonly);
	  if (input_file.readable ())
	    {
	      read_data (input_file, point, abcissa_flag,
			 lower_limit, spacing, symbol_number);
	    }
	}
    }

  ePlotFile plot_file (fileno (stdout));
  if (!save_screen_flag)
    plot_file.erase ();		// we can erase the screen even if there
				// is no data to plot.
  if (point.length () <= 0)
    {				// Exit if we have no data.
      cerr << argv[0] << ": Warning, no data found in input files.\n";
      return (0);
    }
  
  if (debug_flag)
    for (i = point.low (); i < point.fence (); point.next (i))
      {
	cerr << point[i].x sp point[i].y;
	if (point[i].label)
	  cerr sp point[i].label;
	cerr nl;
      };
  
  if (transpose_axes_flag)
    {
      String tmp;
      tmp = y_label;
      y_label = x_label;
      x_label = tmp;
      double t;
      for (i = point.low (); i < point.fence (); point.next (i))
	{
	  t = point[i].y;
	  point[i].y = point[i].x;
	  point[i].x = point[i].y;
	}
    }
  // find the upper and lower limits
  // of the x any y coordinates.
  for (i = point.low (); i < point.fence (); point.next (i))
    {
      if (xmin > point[i].x) xmin = point[i].x;
      if (ymin > point[i].y) ymin = point[i].y;
      if (xmax < point[i].x) xmax = point[i].x;
      if (ymax < point[i].y) ymax = point[i].y;
    }
  // add margins beteen edges of the data and box if range is nonzero.
  double tmp = (ymax - ymin);
  ymax += y_margin * tmp;
  ymin -= y_margin * tmp;
  tmp = (xmax - xmin);
  xmax += x_margin * tmp;
  xmin -= x_margin * tmp;


  // use the ones specified on
  // the command line if present.
  if (x_lower_limit != HUGE) xmin = x_lower_limit;
  if (y_lower_limit != HUGE) ymin = y_lower_limit;
  if (x_upper_limit != HUGE) xmax = x_upper_limit;
  if (y_upper_limit != HUGE) ymax = y_upper_limit;

  // make sure that 0 is not in range if
  // we are using a log scale.
  if (   (x_log_scale
	  && (xmin <= 0.)
	  && (xmax >= 0.))
      || (y_log_scale
	  && (ymin <= 0.)
	  && (ymax >= 0.)))
    {
      cerr << "the lower bound on x is" sp xmin nl;
      cerr << "the upper bound on x is" sp xmax nl;
      cerr << "the lower bound on y is" sp ymin nl;
      cerr << "the upper bound on y is" sp ymax nl;
      cerr << "Zero cannot lie between an upper and lower bound" nl
	"if you use a log scale." nl;
      exit (-1);
    }
  if (x_log_scale)
    {
      log_xmin = log (xmin);
      log_xmax = log (xmax);
    }
  if (y_log_scale)
    {
      log_ymin = log (ymin);
      log_ymax = log (ymax);
    }
  
  plot_file.space (0, 0, plot_size, plot_size);
  // draw a box around the data.
  if (grid_style)
    plot_file.box (px (0.), py (0.), px (1.), py (1.));
  
  char tick_label[32];	// tick lables are less than 16 digits long.
  // draw x tick marks.
  if (grid_style)
    {
      double x_tick =  tick_interval (no_of_ticks, xmin, xmax);
      double x_tick_value = x_tick
	* (x_tick > 0. ? ceil (xmin * A_HAIR_MORE / x_tick)
	   : floor (xmin * A_HAIR_MORE / x_tick));
      while (x_tick_value <= xmax * A_HAIR_MORE)
	{			// tick marks on axes.
	  plot_file.line
	    (px (fx (x_tick_value)), py (0.),
	     px (fx (x_tick_value)), py (-1. * size_of_ticks));
	  plot_file.line
	    (px (fx (x_tick_value)), py (1.),
	     px (fx (x_tick_value)), py (1. + size_of_ticks));
	  sprintf (tick_label, "%g", x_tick_value);
	  plot_file.move
	    (px (fx (x_tick_value) - (extended_plot_format ? 0 :
				      .5 * char_width * strlen (tick_label))),
	     py (-1. * size_of_ticks - (extended_plot_format ? 0 :
					1. * char_height)));
	  if (extended_plot_format)
	    plot_file.alabel (CENTER_JUSTIFY, TOP_FLUSH, tick_label);
	  else
	    plot_file.label (tick_label);
	  if (grid_style == 2)
	    {			// grid across box.
	      plot_file.linemod ("shortdashed");
	      plot_file.line
		(px (fx (x_tick_value)), py (0.),
		 px (fx (x_tick_value)), py (1.));
	      plot_file.linemod ("solid");
	    }
	  if ((.5 < fx (x_tick_value + x_tick))
	      && x_label.length ())
	    {			// put the label between tick marks
	      plot_file.move
		(px (fx (x_tick_value + x_tick / 2.
			 - (extended_plot_format ? 0 : 
			    .5 * char_width * x_label.length ()))),
		 py (-1. * size_of_ticks -
		     (extended_plot_format ? 0 : 2. * char_height)));
	      if (extended_plot_format)
		plot_file.alabel (CENTER_JUSTIFY, TOP_FLUSH, x_label);
	      else
		plot_file.label (x_label);
	      x_label = "";
	    }
	  x_tick_value += x_tick;
	  if (fabs (x_tick_value / x_tick) < 1e-7) x_tick_value = 0.;
	}
      
      double y_tick = tick_interval (no_of_ticks, ymin, ymax);
      double y_tick_value = y_tick
	* (y_tick > 0. ? ceil (ymin * A_HAIR_MORE / y_tick)
	   : floor (ymin * A_HAIR_MORE / y_tick));
      while (y_tick_value <= ymax * A_HAIR_MORE)
	{			// draw tick marks on axes
	  plot_file.line
	    (px (0.), py (fy (y_tick_value)),
	     px (-1. * size_of_ticks), py (fy (y_tick_value)));
	  plot_file.line
	    (px (1.), py (fy (y_tick_value)),
	     px (1. + size_of_ticks), py (fy (y_tick_value)));
	  plot_file.move (px (1. + 2. * size_of_ticks),
			  py (fy (y_tick_value)
			      - .5 * (extended_plot_format ? 0 : char_height)));
	  if (extended_plot_format)
	    plot_file.alabel (LEFT_JUSTIFY, CENTER_FLUSH,
			      sprintf (tick_label, "%g", y_tick_value));
	  else
	    plot_file.label (sprintf (tick_label, "%g", y_tick_value));
	  if (grid_style == 2)
	    {			// draw grid within box.
	      plot_file.linemod ("shortdashed");
	      plot_file.line
		(px (0.), py (fy (y_tick_value)),
		 px (1.), py (fy (y_tick_value)));
	      plot_file.linemod ("solid");
	    }
	  if ((.5 < fy (y_tick_value + y_tick))
	      && y_label.length ())
	    {			// put the label between tick marks
	      plot_file.move
		(px (1. + size_of_ticks),
		 py (fy (y_tick_value + y_tick / 2.)));
	      if (extended_plot_format)
		plot_file.alabel (LEFT_JUSTIFY, CENTER_FLUSH, y_label);
	      else
		plot_file.label (y_label);
	      y_label = "";
	    }
	  y_tick_value += y_tick;
	  if (fabs (y_tick_value / y_tick) < 1e-7) y_tick_value = 0.;
	}
      if (top_label.length ())	// put label above plot.
	{
	  plot_file.move
	    (px (.5 - (extended_plot_format ? 0 :
		       .5 * char_width * top_label.length ())),
	     py (1. + size_of_ticks));
	  if (extended_plot_format)
	    plot_file.alabel (CENTER_JUSTIFY, BOTTOM_FLUSH, top_label);
	  else
	    plot_file.label (top_label);
	}
    }
  
  if (line_style)		// set style of lines connecting data points.
    plot_file.linemod (line_style_name[line_style]);
  
  // draw all the points
  i = point.low ();
  int move = 1;		// move to first point
  double prev_x;	// this should be done with point.prev()
  while (i < point.fence ())
    {				// break line if flag set and x < last x
      if (point[i].x <  prev_x)
	{
	  if (switch_style)
	    {
	      line_style++;
	      plot_file.linemod (line_style_name[line_style]);
	    }
	  if (break_flag)
	    move = 1;
	}
      if (in_box (point[i].x, point[i].y)) // make sure point is inside box
	{				   // we could implement clipping here
	  if (move)
	    {
	      plot_file.move (px (fx (point[i].x)), py (fy (point[i].y)));
	      move = 0;			// only move once for the first point
	    }
	  else
	    plot_file.cont (px (fx (point[i].x)), py (fy (point[i].y)));
	  prev_x = point[i].x;
	}
      point.next (i);
    }
  // now draw all the symbols and data labels
  plot_file.linemod ("solid");
  for (i = point.low (); i < point.fence (); point.next (i))
    {
      if (in_box (point[i].x, point[i].y))
	{
	  if (point[i].label)
	    {
	      plot_file.move
		(px (fx (point[i].x)), py (fy (point[i].y)));
	      if (extended_plot_format)
		plot_file.alabel (CENTER_JUSTIFY, CENTER_FLUSH, point[i].label);
	      else
		plot_file.label (point[i].label);
	    }
	  else if (default_label.length ())
	    {
	      plot_file.move
		(px (fx (point[i].x)), py (fy (point[i].y)));
	      if (extended_plot_format)
		plot_file.alabel (CENTER_JUSTIFY, CENTER_FLUSH, default_label);
	      else
		plot_file.label (default_label);
	    }
	  if (point[i].symbol)
	    {
	      for (int j = 0; (END != symbol[point[i].symbol][j].operation); j++)
		switch (symbol[point[i].symbol][j].operation)
		  {
		  case CONT:
		    plot_file.cont
		      (px (fx (point[i].x) 
			   + symbol_size * symbol[point[i].symbol][j].x),
		       py (fy (point[i].y)
			   + symbol_size * symbol[point[i].symbol][j].y));
		    break;
		  case MOVE:
		    plot_file.move
		      (px (fx (point[i].x)
			   + symbol_size * symbol[point[i].symbol][j].x),
		       py (fy (point[i].y)
			   + symbol_size * symbol[point[i].symbol][j].y));
		    break;
		  case CIRCLE:
		    plot_file.circle
		      (px (fx (point[i].x)), py (fy (point[i].y)),
		       (int) (plot_size * width * symbol_size
			      * symbol[point[i].symbol][j].x));
		    break;
		  }
	    }
	}
    }
  return 0;
}