home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / graphics-0.17 / graph / write_plot.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-03-28  |  19.6 KB  |  662 lines

  1. /* plot, a set of unix plot utilities.
  2.    Copyright (C) 1989 Free Software Foundation, Inc.
  3.  
  4.    Plot is distributed in the hope that it will be useful, but WITHOUT
  5.    ANY WARRANTY.  No author or distributor accepts responsibility to
  6.    anyone for the consequences of using it or for whether it serves any
  7.    particular purpose or works at all, unless he says so in writing.
  8.    Refer to the GNU General Public License for full details.
  9.  
  10.    Everyone is granted permission to copy, modify and redistribute plot,
  11.    but only under the conditions described in the GNU General Public
  12.    License.  A copy of this license is supposed to have been given to you
  13.    along with plot so you can know your rights and responsibilities.  It
  14.    should be in a file named COPYING.  Among other things, the copyright
  15.    notice and this notice must be preserved on all copies.  */
  16.  
  17. /* This file is the main routine for graph. */
  18.  
  19. #include "sys-defines.h"
  20. #include "libplot.h"
  21. #include "extern.h"
  22.  
  23. #define min(a,b) (a < b ? a : b)
  24. #define max(a,b) (a > b ? a : b)
  25.  
  26. /*
  27.  * Select step size to generate nice scale values for plotting.
  28.  * The step size is always 1, 2, or 5.
  29.  *
  30.  * Reference:
  31.  *    Lewart, C. R., "Algorithm 463:  Algorithms SCALE1, SCALE2, and
  32.  *    SCALE3 for Determination of Scales on Computer Generated
  33.  *    Plots[J6],"  Collected Algorithms from CACM.
  34.  *
  35.  */
  36.  
  37. double
  38. scale1 (xmin, xmax, ntics)
  39.      double xmin;        /* Data min        */
  40.      double xmax;        /* Data max        */
  41.      int ntics;            /* Approx tics wanted    */
  42. {
  43.   int k;
  44.   int nal;
  45.   double a, b;
  46.  
  47.   /* The set of valid interval spacings */
  48.   static double vint[] =
  49.   {
  50.     1.0, 2.0, 5.0, 10.0
  51.   };
  52.  
  53.   /* Break points for selecting interval spacing */
  54.  
  55.   static double sqr[] =
  56.   {
  57.     1.414214, 3.162278, 7.071068
  58.   };
  59.  
  60.   /* Find approximate interval size a */
  61.  
  62.   a = (xmax - xmin) / ntics;
  63.   a *= xmax > xmin ? 1. : -1.;
  64.   if (a <= 0.)
  65.     a = 1.;            /* avoid log10 domain errors. */
  66.   nal = (int) floor (log10 (a));
  67.  
  68.   /* Scale a between 1 and 10, and find the closest
  69.      permissible value.  */
  70.  
  71.   b = a * pow (10.0, (double) -nal);
  72.   for (k = 0; k < 3; k++)
  73.     {
  74.       if (b < sqr[k])
  75.     break;
  76.     }
  77.  
  78.   /* Compute interval size */
  79.  
  80.   return (xmax > xmin ? 1. : -1.) * vint[k] * pow (10.0, (double) nal);
  81. }
  82.  
  83. /* log_limits expands the given plot limits to the next power of
  84.    ten. */
  85.  
  86. void
  87. log_limits (val_min, val_max, val_step, round)
  88.      double *val_min;
  89.      double *val_max;
  90.      double *val_step;
  91.      int round;
  92. {
  93.   *val_min = log10 (*val_min);
  94.   *val_max = log10 (*val_max);
  95.   if (round)
  96.     {
  97.       *val_min = ((*val_min < *val_max) ? floor : ceil) (*val_min);
  98.       *val_max = ((*val_min < *val_max) ? ceil : floor) (*val_max);
  99.     }
  100.   *val_step = 1.;
  101. }
  102.  
  103. /* write_plot takes as arguments x, y, and label arrrays containing the data
  104.    and labels, and minimum and maximum x and y values. It scales the
  105.    data and calls the appropriate plot routines. */
  106.  
  107. int
  108. write_plot (out_stream, p, length,
  109.         x_min, x_max, x_spacing, y_min, y_max, y_spacing,
  110.         width, height, up, right,
  111.         symbol_size, tick_size, grid_spec,
  112.         x_label, y_label, top_label,
  113.         margin, log_axis,
  114.         break_flag, round_to_next_tick, default_fontsize,
  115.         omit_labels)
  116.      FILE *out_stream;
  117.      point_struct *p;
  118.      int length;
  119.      double x_min, x_max, x_spacing, y_min, y_max, y_spacing;
  120.      double width, height, up, right;
  121.      double symbol_size;
  122.      double tick_size;
  123.      grid_type grid_spec;
  124.      char *x_label, *y_label, *top_label;
  125.      double margin;
  126.      int log_axis;
  127.      int break_flag;        /* non-zero means break lines after labels. */
  128.      int round_to_next_tick;    /* round limits to the next tick mark */
  129.      int default_fontsize;    /* size in printers points */
  130.      int omit_labels;        /* label both axes unless specified */
  131. {
  132.   /* there is no way you could use longer labels on tick marks! */
  133.   char labelbuf[2048];
  134.   char label_buf[2];        /* for single character labels */
  135.   int index;            /* index into x, y, and label arrays */
  136.   int need_break = 0;        /* non-zero after each label for breaks */
  137.   int is_in_box = 0;        /* non-zero if a point is inside box limits*/
  138.   int plot_width, plot_height, plot_up, plot_right, plot_range;
  139.   double x_range, y_range;
  140.   double xincr, yincr, xval, yval; /* tick spacing and value */
  141.   double x_axis_y_val, y_axis_x_val;    /* position of each axis */
  142.   int i, imax, j, xpos, ypos;    /* tick number, sign of tick increment */
  143.   int linemode_index = -2;    /* only output line mode when it changes */
  144.  
  145. #define no_of_linemodes 6
  146. #define lmod(x) (x % no_of_linemodes)
  147.  
  148.   static char *linemode_labels[] =
  149.     {
  150.       "solid",
  151.       "dotted",
  152.       "shortdashed",
  153.       "dotdashed",
  154.       "longdashed",
  155.       "disconnected",
  156.       };
  157.  
  158. #define plot_size 4096
  159.   /* Check the data for valid log values, if we are using log axes */
  160.  
  161.   if ((log_axis & X_AXIS) && (x_min <= 0))
  162.     {
  163.       (void) fprintf (stderr,
  164.               "%s: error: You cannot have non-positive x values in a log plot\n",
  165.               progname);
  166.       exit (1);
  167.     }
  168.   if ((log_axis & Y_AXIS) && (y_min <= 0))
  169.     {
  170.       (void) fprintf (stderr,
  171.               "%s: Error: You cannot have non-positive y values in a log plot\n",
  172.               progname);
  173.       exit (1);
  174.     }
  175.  
  176.   /* Preprocess the data if we have log axes.  In this case, the values
  177.      we are working with now ALL refer to the log10 values of the data. */
  178.  
  179.   xpos = (x_min < x_max); /* non-zero if min is less than max */
  180.   ypos = (y_min < y_max);
  181.  
  182.   if (log_axis & X_AXIS)
  183.     {
  184.       int k;
  185.       x_range = x_max / x_min;
  186.       x_min /= pow (x_range, margin);
  187.       x_max *= pow (x_range, margin);
  188.  
  189.       for (k = 0; k < length; k++)
  190.     p[k].x = log10 (p[k].x);
  191.       log_limits (&x_min, &x_max, &xincr, round_to_next_tick);
  192.     }
  193.   else
  194.     {
  195.       x_range = x_max - x_min;
  196.       x_min -= x_range * margin;
  197.       x_max += x_range * margin;
  198.  
  199.       if (x_spacing == 0.)
  200.     xincr = scale1 (x_min, x_max, 5);
  201.       else
  202.     xincr = x_spacing;
  203.       if (round_to_next_tick)
  204.     {
  205.       x_min = xincr * (xpos ? floor : ceil) (x_min / xincr);
  206.       x_max = xincr * (xpos ? ceil : floor) (x_max / xincr);
  207.     }
  208.     }
  209.   x_range = x_max - x_min;
  210.  
  211.  
  212.   if (log_axis & Y_AXIS)
  213.     {
  214.       int k;
  215.       y_range = y_max / y_min;
  216.       y_min /= pow (y_range, margin);
  217.       y_max *= pow (y_range, margin);
  218.  
  219.       for (k = 0; k < length; k++)
  220.     p[k].y = log10 (p[k].y);
  221.       log_limits (&y_min, &y_max, &yincr, round_to_next_tick);
  222.     }
  223.   else
  224.     {
  225.       y_range = y_max - y_min;
  226.       y_min -= y_range * margin;
  227.       y_max += y_range * margin;
  228.  
  229.       if (y_spacing == 0.)
  230.     yincr = scale1 (y_min, y_max, 5);
  231.       else
  232.     yincr = y_spacing;
  233.       if (round_to_next_tick)
  234.     {
  235.       y_min = yincr * (ypos ? floor : ceil) (y_min / yincr);
  236.       y_max = yincr * (ypos ? ceil : floor) (y_max / yincr);
  237.     }
  238.     }
  239.   y_range = y_max - y_min;
  240.  
  241.   label_buf[1] = '\0';
  242.  
  243.   openpl ();
  244.   space (0, 0, plot_size, plot_size);
  245.   if (default_fontsize > 0)
  246.     fontsize (default_fontsize);
  247.  
  248.   plot_up = (int)(up * plot_size);
  249.   plot_right = (int)(right * plot_size);
  250.   plot_width = (int)(width * plot_size);
  251.   plot_height = (int)(height * plot_size);
  252.   plot_range = max (plot_height, plot_width);
  253.  
  254.   /* we could optimize this by reducing the two transformations into one. */
  255.  
  256.   /* X Scale: convert from user x values to normalized corrdinates (0 to 1). */
  257. #define XS(x) (((x) - x_min)/x_range)
  258.   /* X Plot: convert from normalized x values to plot corrdinates. */
  259. #define XP(x) ((int) (plot_right + plot_width * (x)))
  260.   /* x value: normalize  and convert to plot coordinates. */
  261. #define XV(x) XP(XS(x))
  262.   /* Same as above, but for plotting log positions */
  263. #define    LXV(x) XP(XS(log10(x)))
  264.  
  265.   /* Y Scale: convert from user y values to normalized corrdinates (0 to 1). */
  266. #define YS(y) (((y) - y_min)/y_range)
  267.   /* Y Plot: convert from normalized y values to plot corrdinates. */
  268. #define YP(y) ((int) (plot_up + plot_height * (y)))
  269.   /* y value: normalize and convert to plot coordinates. */
  270. #define YV(y) YP(YS(y))
  271.   /* Same as above, but for plotting log positions */
  272. #define    LYV(y) YP(YS(log10(y)))
  273.  
  274.   /* Symbol Scale: convert from normalized coordinages to plot coordinates*/
  275. #define SS(x) ((int) (plot_range * (x)))
  276.  
  277.   /* Draw the labels */
  278.   if (top_label != NULL && *top_label != '\0')
  279.     {
  280.       /* tick_size < 0 specifies tics drawn outside the box */
  281.       move (XP(.5), YP(1. + (tick_size >= 0. ? 1. : 2.) * fabs(tick_size)));
  282.       alabel ('c', 'b', top_label);
  283.     }
  284.   if (grid_spec == AXES_AT_ORIGIN)
  285.     {
  286.       /* The position of each axis is at the value 0 (the origin) if
  287.          the value 0 is between the limits of the opposing axis.
  288.          Otherwise, the position is at the end closest to the value of
  289.          0.  We precompute this value here, to make latter code more
  290.          legible.  */
  291.       y_axis_x_val = (x_min * x_max <= 0.) ? 0.
  292.     : (x_min > 0. ? min (x_min, x_max) : max (x_min, x_max));
  293.       x_axis_y_val = (y_min * y_max <= 0.) ? 0.
  294.     : (y_min > 0. ? min (y_min, y_max) : max (y_min, y_max));
  295.     }
  296.  
  297.   linemod ("solid");        /* make sure the axes are solid lines */
  298.   if (x_label != NULL && *x_label != '\0')
  299.     {
  300.       double xmin, xmax;
  301.       int no_of_tics;
  302.  
  303.       xmin = xincr * ((xpos ? ceil : floor) (x_min / xincr));
  304.       xmax = xincr * ((xpos ? floor : ceil) (x_max / xincr));
  305.       no_of_tics = (int) ((xmax - xmin) / xincr);
  306.       
  307.       if (grid_spec == AXES_AT_ORIGIN) 
  308.     /* put the x label on the left end of the axis half way between
  309.        adjacent tick marks. */
  310.     move (XV(x_min + xincr * .5), YV(x_axis_y_val)
  311.           - SS ((tick_size >= 0. ? 3. : 4.) * fabs(tick_size)));
  312.       else
  313.     /* put the x label in the middle of the axis half way between
  314.        adjacent tick marks. */
  315.     move (XV(x_max - xincr * (.5 + (int)(no_of_tics / 2))),
  316.           YV (y_min) - SS ((tick_size >= 0. ? 3. : 4.) * fabs(tick_size)));
  317.       alabel ('c', 't', x_label);
  318.     }
  319.  
  320.   if (y_label != NULL && *y_label != '\0')
  321.     {
  322.       /* put the y label half way between adjacent tick marks in 
  323.      the middle of the axis. */
  324.       double ymin, ymax;
  325.       int no_of_tics;
  326.  
  327.       ymin = yincr * ((ypos ? ceil : floor) (y_min / yincr));
  328.       ymax = yincr * ((ypos ? floor : ceil) (y_max / yincr));
  329.       no_of_tics = (int) ((ymax - ymin) / yincr);
  330.  
  331.       if (grid_spec == AXES_AT_ORIGIN) 
  332.     /* put the y label on the bottom of the axis half way between
  333.        adjacent tick marks. */
  334.     move (XV(y_axis_x_val) - SS((tick_size >= 0. ? 2. : 3.) * fabs(tick_size)),
  335.           YV(y_min + yincr * .5));
  336.       else
  337.     /* put the y label in the middle of the axis half way between
  338.        adjacent tick marks. */
  339.     move (XV(x_min) - SS((tick_size >= 0. ? 2. : 3.) * fabs(tick_size)),
  340.           YV(y_min + yincr * (.5 + (int)(no_of_tics / 2))));
  341.       /* rotate (0, 0, 90); */
  342.       alabel ('r', 'c', y_label);
  343.       /* rotate (0, 0, 0); */
  344.     }
  345.  
  346.   /* Draw a box around the plot.  If TICKS_LEFT_AND_BELOW is specified draw
  347.      only the lower and right hand edges. If NO_GRID then omit the box. */
  348.  
  349.   switch (grid_spec)
  350.     {
  351.     default:
  352.       move (XV(x_max), YV(y_min));
  353.       cont (XV(x_max), YV(y_max));
  354.       cont (XV(x_min), YV(y_max));
  355.     case TICKS_LEFT_AND_BELOW:
  356.       move (XV(x_min), YV(y_max));
  357.       cont (XV(x_min), YV(y_min));
  358.       move (XV(x_min), YV(y_min));
  359.       cont (XV(x_max), YV(y_min));
  360.       break;
  361.     case AXES_AT_ORIGIN:
  362.       move (XV(y_axis_x_val), YV(y_max));
  363.       cont (XV(y_axis_x_val), YV(y_min));
  364.       move (XV(x_min), YV(x_axis_y_val));
  365.       cont (XV(x_max), YV(x_axis_y_val));
  366.     case NO_GRID:;
  367.     }
  368.  
  369.   /* We really need some exact arithmetic here so that we can get the
  370.      precise value for each tick mark.  This avoids generating values like
  371.      1e-15 or -0 as we approach 0. */
  372.   
  373.   i = (int) ((xpos ? ceil : floor) (x_min / xincr));
  374.   imax = (int) ((xpos ? floor : ceil) (x_max / xincr));
  375.   while (i <= imax)
  376.     {
  377.       xval = xincr * i;
  378.       if (!(   (grid_spec == NO_GRID)
  379.         || (omit_labels & X_AXIS)
  380.         || (((xval==y_axis_x_val) || (i==0))
  381.         && (grid_spec == AXES_AT_ORIGIN)
  382.         && (ypos ? (y_min < 0.) : (y_min > 0.)))))
  383.     {
  384.       move (XV (xval),
  385.         YV ((grid_spec == AXES_AT_ORIGIN) ? x_axis_y_val : y_min)
  386.         - SS ((tick_size >= 0. ? .5 : 1.5) * fabs(tick_size)));
  387.       sprintf (labelbuf, "%g", (log_axis & X_AXIS) ?
  388.            pow (10.0, xval) : xval);
  389.       alabel ('c', 't', labelbuf);
  390.     }
  391.       switch (grid_spec)
  392.     {
  393.     case TICKS_ALL_AROUND:
  394.       move (XV (xval), YV (y_max));
  395.       cont (XV (xval), YV (y_max) - SS (tick_size));
  396.     case TICKS_LEFT_AND_BELOW:
  397.       move (XV (xval), YV (y_min));
  398.       cont (XV (xval), YV (y_min) + SS (tick_size));
  399.       break;
  400.       /* put FULL_GRID this into a seperate loop in versions 1.0+
  401.          so that we can issue the linemod only once for the whole
  402.          se of dahsed lines. */
  403.     case FULL_GRID:
  404.       linemod ("shortdashed");
  405.       move (XV (xval), YV (y_min));
  406.       cont (XV (xval), YV (y_max));
  407.       linemod ("solid");
  408.       break;
  409.     case NO_GRID:
  410.       break;
  411.     case AXES_AT_ORIGIN:
  412.       move (XV (xval), YV (x_axis_y_val));
  413.       cont (XV (xval), YV (x_axis_y_val) + SS (tick_size));
  414.     }
  415.       i++;
  416.     }
  417.  
  418.   i = (int) ((ypos ? ceil : floor) (y_min / yincr));
  419.   imax = (int) ((ypos ? floor : ceil) (y_max / yincr));
  420.   while (i <= imax)
  421.     {
  422.       yval = yincr * i;
  423.       if (!(   (grid_spec == NO_GRID)
  424.         || (omit_labels & Y_AXIS)
  425.         || (((yval==x_axis_y_val) || (i==0))
  426.         && (grid_spec == AXES_AT_ORIGIN)
  427.         && (xpos ? (x_min < 0.) : (x_min > 0.)))))
  428.     {
  429.       move (XV((grid_spec == AXES_AT_ORIGIN) ? y_axis_x_val : x_min)
  430.         - SS((tick_size >= 0. ? .5 : 1.5) * fabs(tick_size)),
  431.         YV (yval));
  432.       sprintf (labelbuf, "%g",
  433.            (log_axis & Y_AXIS) ? pow (10.0, yval) : yval);
  434.       alabel ('r', 'c', labelbuf);
  435.     }
  436.       switch (grid_spec)
  437.     {
  438.     case TICKS_ALL_AROUND:
  439.       move (XV (x_max), YV (yval));
  440.       cont (XV (x_max) - SS (tick_size), YV (yval));
  441.     case TICKS_LEFT_AND_BELOW:
  442.       move (XV (x_min), YV (yval));
  443.       cont (XV (x_min) + SS (tick_size), YV (yval));
  444.       break;
  445.     case FULL_GRID:
  446.       linemod ("shortdashed");
  447.       move (XV (x_min), YV (yval));
  448.       cont (XV (x_max), YV (yval));
  449.       linemod ("solid");
  450.       break;
  451.     case NO_GRID:
  452.       break;
  453.     case AXES_AT_ORIGIN:
  454.       move (XV (y_axis_x_val), YV (yval));
  455.       cont (XV (y_axis_x_val) + SS (tick_size), YV (yval));
  456.     }
  457.       i++;
  458.     }
  459.  
  460.   /* Labels, ticks, and grids (if any) have been drawn.  We now want to
  461.      draw some more tick marks if we have a log scale. */
  462.   if ((log_axis & X_AXIS)
  463.       && (grid_spec != NO_GRID)
  464.       && (grid_spec != AXES_AT_ORIGIN))
  465.     {
  466.       double log_tick_size = tick_size * .5;
  467.  
  468.       for (i = (int) (xpos ? floor : ceil) (x_min / xincr);
  469.        xpos ? (i * xincr <= x_max) : (i * xincr >= x_max) ;
  470.        i += xpos ? 1 : -1)
  471.     {
  472.       for (j = 1; j < 10; j++)
  473.         {
  474.           xval = pow (10., xincr * i) * j;
  475.  
  476.           if ((pow(10., x_min) - xval) * (pow(10., x_max) - xval) <= 0.)
  477.         {
  478.           switch (grid_spec)
  479.             {
  480.             case TICKS_ALL_AROUND:
  481.               move (LXV (xval), YV (y_max));
  482.               cont (LXV (xval), YV (y_max) - SS (log_tick_size));
  483.             case TICKS_LEFT_AND_BELOW:
  484.               move (LXV (xval), YV (y_min));
  485.               cont (LXV (xval), YV (y_min) + SS (log_tick_size));
  486.               break;
  487.             case FULL_GRID:
  488.               linemod ("shortdashed");
  489.               move (LXV (xval), YV (y_min));
  490.               cont (LXV (xval), YV (y_max));
  491.               linemod ("solid");
  492.               break;
  493.             case NO_GRID:
  494.             case AXES_AT_ORIGIN:
  495.               break;
  496.             }
  497.         }
  498.         }
  499.     }
  500.     }
  501.  
  502.   if ((log_axis & Y_AXIS)
  503.       && (grid_spec != NO_GRID)
  504.       && (grid_spec != AXES_AT_ORIGIN))
  505.     {
  506.       double log_tick_size = tick_size * .5;
  507.  
  508.       for (i = (int) (ypos ? floor : ceil) (y_min / yincr);
  509.        ypos ? (i * yincr <= y_max) : (i * yincr >= y_max) ;
  510.        i += ypos ? 1 : -1)
  511.     {
  512.       for (j = 1; j < 10; j++)
  513.         {
  514.           yval = pow (10., yincr * i) * j;
  515.  
  516.           if ((pow(10., y_min) - yval) * (pow(10., y_max) - yval) <= 0.)
  517.         {
  518.           switch (grid_spec)
  519.             {
  520.             case TICKS_ALL_AROUND:
  521.               move (XV (x_max), LYV (yval));
  522.               cont (XV (x_max) - SS (log_tick_size), LYV (yval));
  523.             case TICKS_LEFT_AND_BELOW:
  524.               move (XV (x_min), LYV (yval));
  525.               cont (XV (x_min) + SS (log_tick_size), LYV (yval));
  526.               break;
  527.             case FULL_GRID:
  528.               linemod ("shortdashed");
  529.               move (XV (x_min), LYV (yval));
  530.               cont (XV (x_max), LYV (yval));
  531.               linemod ("solid");
  532.               break;
  533.             case NO_GRID:
  534.             case AXES_AT_ORIGIN:
  535.               break;
  536.             }
  537.         }
  538.         }
  539.     }
  540.     }
  541.  
  542.  
  543.   for (index = 0; index < length; index++)
  544.     {
  545.       is_in_box = (((x_min - p[index].x) * (x_max - p[index].x)) <= 0.)
  546.     && (((y_min - p[index].y) * (y_max - p[index].y)) <= 0.);
  547.  
  548.  
  549.       if (linemode_index != p[index].linemode)
  550.     {
  551.       linemode_index = p[index].linemode;
  552.       if (linemode_index >= 0)
  553.         linemod (linemode_labels[lmod (linemode_index)]);
  554.     }
  555.       if (linemode_index < 0)
  556.     move (XV (p[index].x), YV (p[index].y));
  557.       else
  558.     {
  559.       /* we should add clipping code here.  The following just
  560.          omits points outside the box.  It breaks lines where they
  561.          would go outside the box. */
  562.  
  563.       if (! is_in_box)
  564.         {
  565.           need_break = 1;
  566.         }
  567.       else if (need_break)
  568.         {
  569.           move (XV (p[index].x), YV (p[index].y));
  570.           need_break = 0;
  571.         }
  572.       else
  573.         cont (XV (p[index].x), YV (p[index].y));
  574.     }
  575.       if (is_in_box && p[index].string)
  576.     {
  577.       if (linemode_index < 0)
  578.         move (XV (p[index].x), YV (p[index].y));
  579.       alabel ('l', 'c', p[index].string);
  580.       move (XV (p[index].x), YV (p[index].y));
  581.       need_break |= break_flag;
  582.     }
  583.       if (is_in_box && p[index].symbol >= 0)
  584.     {
  585.       if (p[index].symbol > 31)
  586.         {
  587.           /* copy the character symbol into a string and plot it
  588.          as an adjusted label. */
  589.           label_buf[0] = (char) p[index].symbol;
  590.           alabel ('c', 'c', label_buf);
  591.         }
  592.       else
  593.         {
  594.           linemod ("solid");
  595.           switch (p[index].symbol)
  596.         {
  597.         case 0:        /* plus */
  598.           move (XV (p[index].x) - SS (symbol_size), YV (p[index].y));
  599.           cont (XV (p[index].x) + SS (symbol_size), YV (p[index].y));
  600.           move (XV (p[index].x), YV (p[index].y) - SS (symbol_size));
  601.           cont (XV (p[index].x), YV (p[index].y) + SS (symbol_size));
  602.           break;
  603.         case 1:        /* cross */
  604.           move (XV (p[index].x) - SS (symbol_size),
  605.             YV (p[index].y) - SS (symbol_size));
  606.           cont (XV (p[index].x) + SS (symbol_size),
  607.             YV (p[index].y) + SS (symbol_size));
  608.           move (XV (p[index].x) + SS (symbol_size),
  609.             YV (p[index].y) - SS (symbol_size));
  610.           cont (XV (p[index].x) - SS (symbol_size),
  611.             YV (p[index].y) + SS (symbol_size));
  612.           break;
  613.         case 2:        /* star */
  614.           move (XV (p[index].x) - SS (symbol_size), YV (p[index].y));
  615.           cont (XV (p[index].x) + SS (symbol_size), YV (p[index].y));
  616.           move (XV (p[index].x) - SS (symbol_size * .5),
  617.             YV (p[index].y) + SS (symbol_size * .866));
  618.           cont (XV (p[index].x) + SS (symbol_size * .5),
  619.             YV (p[index].y) - SS (symbol_size * .866));
  620.           move (XV (p[index].x) + SS (symbol_size * .5),
  621.             YV (p[index].y) + SS (symbol_size * .866));
  622.           cont (XV (p[index].x) - SS (symbol_size * .5),
  623.             YV (p[index].y) - SS (symbol_size * .866));
  624.           break;
  625.         case 3:        /* box */
  626.           move (XV (p[index].x) - SS (symbol_size),
  627.             YV (p[index].y) - SS (symbol_size));
  628.           cont (XV (p[index].x) + SS (symbol_size),
  629.             YV (p[index].y) - SS (symbol_size));
  630.           cont (XV (p[index].x) + SS (symbol_size),
  631.             YV (p[index].y) + SS (symbol_size));
  632.           cont (XV (p[index].x) - SS (symbol_size),
  633.             YV (p[index].y) + SS (symbol_size));
  634.           cont (XV (p[index].x) - SS (symbol_size),
  635.             YV (p[index].y) - SS (symbol_size));
  636.           break;
  637.         case 4:        /* diamond */
  638.           move (XV (p[index].x),
  639.             YV (p[index].y) - SS (symbol_size));
  640.           cont (XV (p[index].x) + SS (symbol_size),
  641.             YV (p[index].y));
  642.           cont (XV (p[index].x),
  643.             YV (p[index].y) + SS (symbol_size));
  644.           cont (XV (p[index].x) - SS (symbol_size),
  645.             YV (p[index].y));
  646.           cont (XV (p[index].x),
  647.             YV (p[index].y) - SS (symbol_size));
  648.           break;
  649.         case 5:        /* circle */
  650.           circle (XV (p[index].x), YV (p[index].y), SS (symbol_size));
  651.           break;
  652.         }
  653.           if (linemode_index >= 0)
  654.         linemod (linemode_labels[lmod (linemode_index)]);
  655.           move (XV (p[index].x), YV (p[index].y));
  656.         }
  657.     }
  658.     }
  659.   closepl ();
  660.   return 0;
  661. }
  662.