home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 334_01 / graphics.c < prev    next >
Text File  |  1991-02-05  |  37KB  |  1,309 lines

  1. /* GNUPLOT - graphics.c */
  2. /*
  3.  * Copyright (C) 1986, 1987, 1990   Thomas Williams, Colin Kelley
  4.  *
  5.  * Permission to use, copy, and distribute this software and its
  6.  * documentation for any purpose with or without fee is hereby granted, 
  7.  * provided that the above copyright notice appear in all copies and 
  8.  * that both that copyright notice and this permission notice appear 
  9.  * in supporting documentation.
  10.  *
  11.  * Permission to modify the software is granted, but not the right to
  12.  * distribute the modified code.  Modifications are to be distributed 
  13.  * as patches to released version.
  14.  *  
  15.  * This software  is provided "as is" without express or implied warranty.
  16.  * 
  17.  *
  18.  * AUTHORS
  19.  * 
  20.  *   Original Software:
  21.  *     Thomas Williams,  Colin Kelley.
  22.  * 
  23.  *   Gnuplot 2.0 additions:
  24.  *       Russell Lang, Dave Kotz, John Campbell.
  25.  * 
  26.  * send your comments or suggestions to (pixar!info-gnuplot@sun.com).
  27.  * 
  28.  */
  29.  
  30. #include <stdio.h>
  31. #include <math.h>
  32. #include <assert.h>
  33. #include "plot.h"
  34. #include "setshow.h"
  35.  
  36. extern char *strcpy(),*strncpy(),*strcat();
  37.  
  38. void plot_impulses();
  39. void plot_lines();
  40. void plot_points();
  41. void plot_dots();
  42. void edge_intersect();
  43. BOOLEAN two_edge_intersect();
  44.  
  45. #ifndef max        /* Lattice C has max() in math.h, but shouldn't! */
  46. #define max(a,b) ((a > b) ? a : b)
  47. #endif
  48.  
  49. #ifndef min
  50. #define min(a,b) ((a < b) ? a : b)
  51. #endif
  52.  
  53. #define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )
  54.  
  55. /* Define the boundary of the plot
  56.  * These are computed at each call to do_plot, and are constant over
  57.  * the period of one do_plot. They actually only change when the term
  58.  * type changes and when the 'set size' factors change. 
  59.  */
  60. static int xleft, xright, ybot, ytop;
  61.  
  62. /* Boundary and scale factors, in user coordinates */
  63. /* x_min, x_max, y_min, y_max are local to this file and
  64.  * are not the same as variables of the same names in other files
  65.  */
  66. static double x_min, x_max, y_min, y_max;
  67. static double xscale, yscale;
  68.  
  69. /* And the functions to map from user to terminal coordinates */
  70. #define map_x(x) (int)(xleft+(x-x_min)*xscale+0.5) /* maps floating point x to screen */ 
  71. #define map_y(y) (int)(ybot+(y-y_min)*yscale+0.5)    /* same for y */
  72.  
  73. /* (DFK) Watch for cancellation error near zero on axes labels */
  74. #define SIGNIF (0.01)        /* less than one hundredth of a tic mark */
  75. #define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
  76. #define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
  77.  
  78. /* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog 
  79.  * macro, so I write it as a function on that machine.
  80.  */
  81. #ifndef sun386
  82. /* (DFK) Use 10^x if logscale is in effect, else x */
  83. #define CheckLog(log, x) ((log) ? pow(10., (x)) : (x))
  84. #else
  85. static double
  86. CheckLog(log, x)
  87.      BOOLEAN log;
  88.      double x;
  89. {
  90.   if (log)
  91.     return(pow(10., x));
  92.   else
  93.     return(x);
  94. }
  95. #endif /* sun386 */
  96.  
  97. double
  98. LogScale(coord, islog, what, axis)
  99.     double coord;            /* the value */
  100.     BOOLEAN islog;            /* is this axis in logscale? */
  101.     char *what;            /* what is the coord for? */
  102.     char *axis;            /* which axis is this for ("x" or "y")? */
  103. {
  104.     if (islog) {
  105.        if (coord <= 0.0) {
  106.           char errbuf[100];        /* place to write error message */
  107.         (void) sprintf(errbuf,"%s has %s coord of %g; must be above 0 for log scale!",
  108.                 what, axis, coord);
  109.           (*term_tbl[term].text)();
  110.           (void) fflush(outfile);
  111.           int_error(errbuf, NO_CARET);
  112.        } else
  113.         return(log10(coord));
  114.     }
  115.     return(coord);
  116. }
  117.  
  118. /* borders of plotting area */
  119. /* computed once on every call to do_plot */
  120. boundary(scaling)
  121.     BOOLEAN scaling;        /* TRUE if terminal is doing the scaling */
  122. {
  123.     register struct termentry *t = &term_tbl[term];
  124.     xleft = (t->h_char)*12;
  125.     xright = (scaling ? 1 : xsize) * (t->xmax) - (t->h_char)*2 - (t->h_tic);
  126.     ybot = (t->v_char)*5/2 + 1;
  127.     ytop = (scaling ? 1 : ysize) * (t->ymax) - (t->v_char)*3/2 - 1;
  128. }
  129.  
  130.  
  131. double dbl_raise(x,y)
  132. double x;
  133. int y;
  134. {
  135. register int i;
  136. double val;
  137.  
  138.     val = 1.0;
  139.     for (i=0; i < abs(y); i++)
  140.         val *= x;
  141.     if (y < 0 ) return (1.0/val);
  142.     return(val);
  143. }
  144.  
  145.  
  146. double make_tics(tmin,tmax,logscale)
  147. double tmin,tmax;
  148. BOOLEAN logscale;
  149. {
  150. register double xr,xnorm,tics,tic,l10;
  151.  
  152.     xr = fabs(tmin-tmax);
  153.     
  154.     l10 = log10(xr);
  155.     if (logscale) {
  156.         tic = dbl_raise(10.0,(l10 >= 0.0 ) ? (int)l10 : ((int)l10-1));
  157.         if (tic < 1.0)
  158.             tic = 1.0;
  159.     } else {
  160.         xnorm = pow(10.0,l10-(double)((l10 >= 0.0 ) ? (int)l10 : ((int)l10-1)));
  161.         if (xnorm <= 2)
  162.             tics = 0.2;
  163.         else if (xnorm <= 5)
  164.             tics = 0.5;
  165.         else tics = 1.0;    
  166.         tic = tics * dbl_raise(10.0,(l10 >= 0.0 ) ? (int)l10 : ((int)l10-1));
  167.     }
  168.     return(tic);
  169. }
  170.  
  171.  
  172. do_plot(plots, pcount, min_x, max_x, min_y, max_y)
  173. struct curve_points *plots;
  174. int pcount;            /* count of plots in linked list */
  175. double min_x, max_x;
  176. double min_y, max_y;
  177. {
  178. register struct termentry *t = &term_tbl[term];
  179. register int curve, xaxis_y, yaxis_x;
  180. register struct curve_points *this_plot;
  181. register double ytic, xtic;
  182. register int xl, yl;
  183.             /* only a Pyramid would have this many registers! */
  184. double xtemp, ytemp;
  185. struct text_label *this_label;
  186. struct arrow_def *this_arrow;
  187. BOOLEAN scaling;
  188.  
  189. /* store these in variables global to this file */
  190. /* otherwise, we have to pass them around a lot */
  191.      x_min = min_x;
  192.      x_max = max_x; 
  193.      y_min = min_y;
  194.      y_max = max_y;
  195.  
  196.     if (polar) {
  197.         /* will possibly change x_min, x_max, y_min, y_max */
  198.         polar_xform(plots,pcount);
  199.     }
  200.  
  201.     if (y_min == VERYLARGE || y_max == -VERYLARGE)
  202.         int_error("all points undefined!", NO_CARET);
  203.  
  204.     if (x_min == VERYLARGE || x_max == -VERYLARGE)
  205.         int_error("all points undefined!", NO_CARET);
  206.  
  207. /*    Apply the desired viewport offsets. */
  208.      if (y_min < y_max) {
  209.         y_min -= boff;
  210.         y_max += toff;
  211.     } else {
  212.         y_max -= boff;
  213.         y_min += toff;
  214.     }
  215.      if (x_min < x_max) {
  216.         x_min -= loff;
  217.         x_max += roff;
  218.     } else {
  219.         x_max -= loff;
  220.         x_min += roff;
  221.     }
  222.  
  223. /* SETUP RANGES, SCALES AND TIC PLACES */
  224.     if (ytics && yticdef.type == TIC_COMPUTED) {
  225.        ytic = make_tics(y_min,y_max,log_y);
  226.     
  227.        if (autoscale_ly) {
  228.           if (y_min < y_max) {
  229.              y_min = ytic * floor(y_min/ytic);       
  230.              y_max = ytic * ceil(y_max/ytic);
  231.           }
  232.           else {            /* reverse axis */
  233.              y_min = ytic * ceil(y_min/ytic);       
  234.              y_max = ytic * floor(y_max/ytic);
  235.           }
  236.        }
  237.     }
  238.  
  239.     if (xtics && xticdef.type == TIC_COMPUTED) {
  240.        xtic = make_tics(x_min,x_max,log_x);
  241.        
  242.        if (autoscale_lx) {
  243.           if (x_min < x_max) {
  244.              x_min = xtic * floor(x_min/xtic);    
  245.              x_max = xtic * ceil(x_max/xtic);
  246.           } else {
  247.              x_min = xtic * ceil(x_min/xtic);
  248.              x_max = xtic * floor(x_max/xtic);    
  249.           }
  250.        }
  251.     }
  252.  
  253. /*    This used be x_max == x_min, but that caused an infinite loop once. */
  254.     if (fabs(x_max - x_min) < zero)
  255.         int_error("x_min should not equal x_max!",NO_CARET);
  256.     if (fabs(y_max - y_min) < zero)
  257.         int_error("y_min should not equal y_max!",NO_CARET);
  258.  
  259. /* INITIALIZE TERMINAL */
  260.     if (!term_init) {
  261.         (*t->init)();
  262.         term_init = TRUE;
  263.     }
  264.     screen_ok = FALSE;
  265.      scaling = (*t->scale)(xsize, ysize);
  266.     (*t->graphics)();
  267.  
  268.      /* now compute boundary for plot (xleft, xright, ytop, ybot) */
  269.      boundary(scaling);
  270.  
  271. /* SCALE FACTORS */
  272.     yscale = (ytop - ybot)/(y_max - y_min);
  273.     xscale = (xright - xleft)/(x_max - x_min);
  274.     
  275. /* DRAW AXES */
  276.     (*t->linetype)(-1);    /* axis line type */
  277.     xaxis_y = map_y(0.0);
  278.     yaxis_x = map_x(0.0); 
  279.  
  280.     if (xaxis_y < ybot)
  281.         xaxis_y = ybot;                /* save for impulse plotting */
  282.     else if (xaxis_y >= ytop)
  283.         xaxis_y = ytop ;
  284.     else if (xzeroaxis && !log_y) {
  285.         (*t->move)(xleft,xaxis_y);
  286.         (*t->vector)(xright,xaxis_y);
  287.     }
  288.  
  289.     if (yzeroaxis && !log_x && yaxis_x >= xleft && yaxis_x < xright ) {
  290.         (*t->move)(yaxis_x,ybot);
  291.         (*t->vector)(yaxis_x,ytop);
  292.     }
  293.  
  294. /* DRAW TICS */
  295.     (*t->linetype)(-2); /* border linetype */
  296.  
  297.     /* label y axis tics */
  298.      if (ytics) {
  299.         switch (yticdef.type) {
  300.            case TIC_COMPUTED: {
  301.                if (y_min < y_max)
  302.                 draw_ytics(ytic * floor(y_min/ytic),
  303.                         ytic,
  304.                         ytic * ceil(y_max/ytic));
  305.               else
  306.                 draw_ytics(ytic * floor(y_max/ytic),
  307.                         ytic,
  308.                         ytic * ceil(y_min/ytic));
  309.  
  310.               break;
  311.            }
  312.            case TIC_SERIES: {
  313.               draw_series_ytics(yticdef.def.series.start, 
  314.                             yticdef.def.series.incr, 
  315.                             yticdef.def.series.end);
  316.               break;
  317.            }
  318.            case TIC_USER: {
  319.               draw_set_ytics(yticdef.def.user);
  320.               break;
  321.            }
  322.            default: {
  323.               (*t->text)();
  324.                 (void) fflush(outfile);
  325.               int_error("unknown tic type in yticdef in do_plot", NO_CARET);
  326.               break;        /* NOTREACHED */
  327.            }
  328.         }
  329.     }
  330.  
  331.     /* label x axis tics */
  332.      if (xtics) {
  333.         switch (xticdef.type) {
  334.            case TIC_COMPUTED: {
  335.                if (x_min < x_max)
  336.                 draw_xtics(xtic * floor(x_min/xtic),
  337.                         xtic,
  338.                         xtic * ceil(x_max/xtic));
  339.               else
  340.                 draw_xtics(xtic * floor(x_max/xtic),
  341.                         xtic,
  342.                         xtic * ceil(x_min/xtic));
  343.  
  344.               break;
  345.            }
  346.            case TIC_SERIES: {
  347.               draw_series_xtics(xticdef.def.series.start, 
  348.                             xticdef.def.series.incr, 
  349.                             xticdef.def.series.end);
  350.               break;
  351.            }
  352.            case TIC_USER: {
  353.               draw_set_xtics(xticdef.def.user);
  354.               break;
  355.            }
  356.            default: {
  357.               (*t->text)();
  358.               (void) fflush(outfile);
  359.               int_error("unknown tic type in xticdef in do_plot", NO_CARET);
  360.               break;        /* NOTREACHED */
  361.            }
  362.         }
  363.     }
  364.  
  365. /* DRAW PLOT BORDER */
  366.     (*t->linetype)(-2); /* border linetype */
  367.     (*t->move)(xleft,ybot);    
  368.     (*t->vector)(xright,ybot);    
  369.     (*t->vector)(xright,ytop);    
  370.     (*t->vector)(xleft,ytop);    
  371.     (*t->vector)(xleft,ybot);
  372.  
  373. /* PLACE YLABEL */
  374.     if (*ylabel != NULL) {
  375.        if ((*t->text_angle)(1)) { 
  376.           if ((*t->justify_text)(CENTRE)) { 
  377.              (*t->put_text)((t->v_char),
  378.                          (ytop+ybot)/2, ylabel);
  379.           }
  380.           else {
  381.              (*t->put_text)((t->v_char),
  382.                          (ytop+ybot)/2-(t->h_char)*strlen(ylabel)/2, 
  383.                          ylabel);
  384.           }
  385.        }
  386.        else {
  387.           (void)(*t->justify_text)(LEFT);
  388.           (*t->put_text)(0,ytop+(t->v_char), ylabel);
  389.        }
  390.        (void)(*t->text_angle)(0);
  391.     }
  392.  
  393. /* PLACE XLABEL */
  394.     if (*xlabel != NULL) {
  395.        if ((*t->justify_text)(CENTRE)) 
  396.         (*t->put_text)( (xleft+xright)/2,
  397.                     ybot-2*(t->v_char), xlabel);
  398.        else
  399.         (*t->put_text)( (xleft+xright)/2 - strlen(xlabel)*(t->h_char)/2,
  400.                     ybot-2*(t->v_char), xlabel);
  401.     }
  402.  
  403. /* PLACE TITLE */
  404.     if (*title != NULL) {
  405.        if ((*t->justify_text)(CENTRE)) 
  406.         (*t->put_text)( (xleft+xright)/2, 
  407.                     ytop+(t->v_char), title);
  408.        else
  409.         (*t->put_text)( (xleft+xright)/2 - strlen(title)*(t->h_char)/2,
  410.                     ytop+(t->v_char), title);
  411.     }
  412.  
  413. /* PLACE LABELS */
  414.     for (this_label = first_label; this_label!=NULL;
  415.             this_label=this_label->next ) {
  416.          xtemp = LogScale(this_label->x, log_x, "label", "x");
  417.          ytemp = LogScale(this_label->y, log_y, "label", "y");
  418.         if ((*t->justify_text)(this_label->pos)) {
  419.             (*t->put_text)(map_x(xtemp),map_y(ytemp),this_label->text);
  420.         }
  421.         else {
  422.             switch(this_label->pos) {
  423.                 case  LEFT:
  424.                     (*t->put_text)(map_x(xtemp),map_y(ytemp),
  425.                         this_label->text);
  426.                     break;
  427.                 case CENTRE:
  428.                     (*t->put_text)(map_x(xtemp)-
  429.                         (t->h_char)*strlen(this_label->text)/2,
  430.                         map_y(ytemp), this_label->text);
  431.                     break;
  432.                 case RIGHT:
  433.                     (*t->put_text)(map_x(xtemp)-
  434.                         (t->h_char)*strlen(this_label->text),
  435.                         map_y(ytemp), this_label->text);
  436.                     break;
  437.             }
  438.          }
  439.      }
  440.  
  441. /* PLACE ARROWS */
  442.     (*t->linetype)(0);    /* arrow line type */
  443.     for (this_arrow = first_arrow; this_arrow!=NULL;
  444.         this_arrow = this_arrow->next ) {
  445.        int sx = map_x(LogScale(this_arrow->sx, log_x, "arrow", "x"));
  446.        int sy = map_y(LogScale(this_arrow->sy, log_y, "arrow", "y"));
  447.        int ex = map_x(LogScale(this_arrow->ex, log_x, "arrow", "x"));
  448.        int ey = map_y(LogScale(this_arrow->ey, log_y, "arrow", "y"));
  449.        
  450.        (*t->arrow)(sx, sy, ex, ey);
  451.     }
  452.  
  453.  
  454. /* DRAW CURVES */
  455.     if (key == -1) {
  456.         xl = xright  - (t->h_tic) - (t->h_char)*5;
  457.         yl = ytop - (t->v_tic) - (t->v_char);
  458.     }
  459.     if (key == 1) {
  460.         xl = map_x( LogScale(key_x, log_x, "key", "x") );
  461.         yl = map_y( LogScale(key_y, log_y, "key", "y") );
  462.     }
  463.  
  464.     this_plot = plots;
  465.     for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
  466.         (*t->linetype)(this_plot->line_type);
  467.         if (key != 0) {
  468.             if ((*t->justify_text)(RIGHT)) {
  469.                 (*t->put_text)(xl,
  470.                     yl,this_plot->title);
  471.             }
  472.             else {
  473.                 if (inrange(xl-(t->h_char)*strlen(this_plot->title), 
  474.                          xleft, xright))
  475.                  (*t->put_text)(xl-(t->h_char)*strlen(this_plot->title),
  476.                              yl,this_plot->title);
  477.             }
  478.         }
  479.  
  480.         switch(this_plot->plot_style) {
  481.             case IMPULSES: {
  482.                if (key != 0) {
  483.                   (*t->move)(xl+(t->h_char),yl);
  484.                   (*t->vector)(xl+4*(t->h_char),yl);
  485.                }
  486.                plot_impulses(this_plot, yaxis_x, xaxis_y);
  487.                break;
  488.             }
  489.             case LINES: {
  490.                if (key != 0) {
  491.                   (*t->move)(xl+(int)(t->h_char),yl);
  492.                   (*t->vector)(xl+(int)(4*(t->h_char)),yl);
  493.                }
  494.                plot_lines(this_plot);
  495.                break;
  496.             }
  497.             case POINTS: {
  498.                if (key != 0) {
  499.                   (*t->point)(xl+2*(t->h_char),yl,
  500.                             this_plot->point_type);
  501.                }
  502.                plot_points(this_plot);
  503.                break;
  504.             }
  505.             case LINESPOINTS: {
  506.                /* put lines */
  507.                if (key != 0) {
  508.                   (*t->move)(xl+(t->h_char),yl);
  509.                   (*t->vector)(xl+4*(t->h_char),yl);
  510.                }
  511.                plot_lines(this_plot);
  512.  
  513.                /* put points */
  514.                if (key != 0) {
  515.                   (*t->point)(xl+2*(t->h_char),yl,
  516.                             this_plot->point_type);
  517.                }
  518.                plot_points(this_plot);
  519.                break;
  520.             }
  521.             case DOTS: {
  522.                if (key != 0) {
  523.                   (*t->point)(xl+2*(t->h_char),yl, -1);
  524.                }
  525.                plot_dots(this_plot);
  526.                break;
  527.             }
  528.         }
  529.         yl = yl - (t->v_char);
  530.     }
  531.     (*t->text)();
  532.     (void) fflush(outfile);
  533. }
  534.  
  535. /* plot_impulses:
  536.  * Plot the curves in IMPULSES style
  537.  */
  538. void
  539. plot_impulses(plot, yaxis_x, xaxis_y)
  540.     struct curve_points *plot;
  541.     int yaxis_x, xaxis_y;
  542. {
  543.     int i;
  544.     int x,y;
  545.     struct termentry *t = &term_tbl[term];
  546.  
  547.     for (i = 0; i < plot->p_count; i++) {
  548.        switch (plot->points[i].type) {
  549.           case INRANGE: {
  550.              x = map_x(plot->points[i].x);
  551.              y = map_y(plot->points[i].y);
  552.              break;
  553.           }
  554.           case OUTRANGE: {
  555.              if (!inrange(plot->points[i].x, x_min,x_max))
  556.                continue;
  557.              x = map_x(plot->points[i].x);
  558.              if ((y_min < y_max 
  559.                  && plot->points[i].y < y_min)
  560.                 || (y_max < y_min 
  561.                     && plot->points[i].y > y_min))
  562.                y = map_y(y_min);
  563.              if ((y_min < y_max 
  564.                  && plot->points[i].y > y_max)
  565.                 || (y_max<y_min 
  566.                     && plot->points[i].y < y_max))
  567.                y = map_y(y_max);
  568.              break;
  569.           }
  570.           default:        /* just a safety */
  571.           case UNDEFINED: {
  572.              continue;
  573.           }
  574.        }
  575.                     
  576.        if (polar)
  577.           (*t->move)(yaxis_x,xaxis_y);
  578.        else
  579.           (*t->move)(x,xaxis_y);
  580.        (*t->vector)(x,y);
  581.     }
  582.  
  583. }
  584.  
  585. /* plot_lines:
  586.  * Plot the curves in LINES style
  587.  */
  588. void
  589. plot_lines(plot)
  590.     struct curve_points *plot;
  591. {
  592.     int i;                /* point index */
  593.     int x,y;                /* point in terminal coordinates */
  594.     struct termentry *t = &term_tbl[term];
  595.     enum coord_type prev = UNDEFINED; /* type of previous point */
  596.     double ex, ey;            /* an edge point */
  597.     double lx[2], ly[2];        /* two edge points */
  598.  
  599.     for (i = 0; i < plot->p_count; i++) {
  600.        switch (plot->points[i].type) {
  601.           case INRANGE: {
  602.              x = map_x(plot->points[i].x);
  603.              y = map_y(plot->points[i].y);
  604.  
  605.              if (prev == INRANGE) {
  606.                 (*t->vector)(x,y);
  607.              } else if (prev == OUTRANGE) {
  608.                 /* from outrange to inrange */
  609.                 if (!clip_lines1) {
  610.                     (*t->move)(x,y);
  611.                 } else {
  612.                     edge_intersect(plot->points, i, &ex, &ey);
  613.                     (*t->move)(map_x(ex), map_y(ey));
  614.                     (*t->vector)(x,y);
  615.                 }
  616.              } else {        /* prev == UNDEFINED */
  617.                 (*t->move)(x,y);
  618.                 (*t->vector)(x,y);
  619.              }
  620.                     
  621.              break;
  622.           }
  623.           case OUTRANGE: {
  624.              if (prev == INRANGE) {
  625.                 /* from inrange to outrange */
  626.                 if (clip_lines1) {
  627.                     edge_intersect(plot->points, i, &ex, &ey);
  628.                     (*t->vector)(map_x(ex), map_y(ey));
  629.                 }
  630.              } else if (prev == OUTRANGE) {
  631.                 /* from outrange to outrange */
  632.                 if (clip_lines2) {
  633.                     if (two_edge_intersect(plot->points, i, lx, ly)) {
  634.                        (*t->move)(map_x(lx[0]), map_y(ly[0]));
  635.                        (*t->vector)(map_x(lx[1]), map_y(ly[1]));
  636.                     }
  637.                 }
  638.              }
  639.              break;
  640.           }
  641.           default:        /* just a safety */
  642.           case UNDEFINED: {
  643.              break;
  644.           }
  645.        }
  646.        prev = plot->points[i].type;
  647.     }
  648. }
  649.  
  650. /* plot_points:
  651.  * Plot the curves in POINTS style
  652.  */
  653. void
  654. plot_points(plot)
  655.     struct curve_points *plot;
  656. {
  657.     int i;
  658.     int x,y;
  659.     struct termentry *t = &term_tbl[term];
  660.  
  661.     for (i = 0; i < plot->p_count; i++) {
  662.        if (plot->points[i].type == INRANGE) {
  663.           x = map_x(plot->points[i].x);
  664.           y = map_y(plot->points[i].y);
  665.           /* do clipping if necessary */
  666.           if (!clip_points ||
  667.              (   x >= xleft + t->h_tic  && y >= ybot + t->v_tic 
  668.               && x <= xright - t->h_tic && y <= ytop - t->v_tic))
  669.             (*t->point)(x,y, plot->point_type);
  670.        }
  671.     }
  672. }
  673.  
  674. /* plot_dots:
  675.  * Plot the curves in DOTS style
  676.  */
  677. void
  678. plot_dots(plot)
  679.     struct curve_points *plot;
  680. {
  681.     int i;
  682.     int x,y;
  683.     struct termentry *t = &term_tbl[term];
  684.  
  685.     for (i = 0; i < plot->p_count; i++) {
  686.        if (plot->points[i].type == INRANGE) {
  687.           x = map_x(plot->points[i].x);
  688.           y = map_y(plot->points[i].y);
  689.           /* point type -1 is a dot */
  690.           (*t->point)(x,y, -1);
  691.        }
  692.     }
  693. }
  694.  
  695. /* single edge intersection algorithm */
  696. /* Given two points, one inside and one outside the plot, return
  697.  * the point where an edge of the plot intersects the line segment defined 
  698.  * by the two points.
  699.  */
  700. void
  701. edge_intersect(points, i, ex, ey)
  702.     struct coordinate *points; /* the points array */
  703.     int i;                /* line segment from point i-1 to point i */
  704.     double *ex, *ey;        /* the point where it crosses an edge */
  705. {
  706.     /* global x_min, x_max, y_min, x_max */
  707.     double ax = points[i-1].x;
  708.     double ay = points[i-1].y;
  709.     double bx = points[i].x;
  710.     double by = points[i].y;
  711.     double x, y;            /* possible intersection point */
  712.  
  713.     if (by == ay) {
  714.        /* horizontal line */
  715.        /* assume inrange(by, y_min, y_max) */
  716.        *ey = by;        /* == ay */
  717.  
  718.        if (inrange(x_max, ax, bx))
  719.         *ex = x_max;
  720.        else if (inrange(x_min, ax, bx))
  721.         *ex = x_min;
  722.        else {
  723.         (*term_tbl[term].text)();
  724.         (void) fflush(outfile);
  725.         int_error("error in edge_intersect", NO_CARET);
  726.        }
  727.        return;
  728.     } else if (bx == ax) {
  729.        /* vertical line */
  730.        /* assume inrange(bx, x_min, x_max) */
  731.        *ex = bx;        /* == ax */
  732.  
  733.        if (inrange(y_max, ay, by))
  734.         *ey = y_max;
  735.        else if (inrange(y_min, ay, by))
  736.         *ey = y_min;
  737.        else {
  738.         (*term_tbl[term].text)();
  739.         (void) fflush(outfile);
  740.         int_error("error in edge_intersect", NO_CARET);
  741.        }
  742.        return;
  743.     }
  744.  
  745.     /* slanted line of some kind */
  746.  
  747.     /* does it intersect y_min edge */
  748.     if (inrange(y_min, ay, by) && y_min != ay && y_min != by) {
  749.        x = ax + (y_min-ay) * ((bx-ax) / (by-ay));
  750.        if (inrange(x, x_min, x_max)) {
  751.           *ex = x;
  752.           *ey = y_min;
  753.           return;            /* yes */
  754.        }
  755.     }
  756.     
  757.     /* does it intersect y_max edge */
  758.     if (inrange(y_max, ay, by) && y_max != ay && y_max != by) {
  759.        x = ax + (y_max-ay) * ((bx-ax) / (by-ay));
  760.        if (inrange(x, x_min, x_max)) {
  761.           *ex = x;
  762.           *ey = y_max;
  763.           return;            /* yes */
  764.        }
  765.     }
  766.  
  767.     /* does it intersect x_min edge */
  768.     if (inrange(x_min, ax, bx) && x_min != ax && x_min != bx) {
  769.        y = ay + (x_min-ax) * ((by-ay) / (bx-ax));
  770.        if (inrange(y, y_min, y_max)) {
  771.           *ex = x_min;
  772.           *ey = y;
  773.           return;
  774.        }
  775.     }
  776.  
  777.     /* does it intersect x_max edge */
  778.     if (inrange(x_max, ax, bx) && x_max != ax && x_max != bx) {
  779.        y = ay + (x_max-ax) * ((by-ay) / (bx-ax));
  780.        if (inrange(y, y_min, y_max)) {
  781.           *ex = x_max;
  782.           *ey = y;
  783.           return;
  784.        }
  785.     }
  786.  
  787.     /* It is possible for one or two of the [ab][xy] values to be -VERYLARGE.
  788.     * If ax=bx=-VERYLARGE or ay=by=-VERYLARGE we have already returned 
  789.     * FALSE above. Otherwise we fall through all the tests above. 
  790.     * If two are -VERYLARGE, it is ax=ay=-VERYLARGE or bx=by=-VERYLARGE 
  791.     * since either a or b must be INRANGE. 
  792.     * Note that for ax=ay=-VERYLARGE or bx=by=-VERYLARGE we can do nothing.
  793.     * Handle them carefully here. As yet we have no way for them to be 
  794.     * +VERYLARGE.
  795.     */
  796.     if (ax == -VERYLARGE) {
  797.        if (ay != -VERYLARGE) {
  798.           *ex = min(x_min, x_max);
  799.           *ey = by;
  800.           return;
  801.        }
  802.     } else if (bx == -VERYLARGE) {
  803.        if (by != -VERYLARGE) {
  804.           *ex = min(x_min, x_max);
  805.           *ey = ay;
  806.           return;
  807.        }
  808.     } else if (ay == -VERYLARGE) {
  809.        /* note we know ax != -VERYLARGE */
  810.        *ex = bx;
  811.        *ey = min(y_min, y_max);
  812.        return;
  813.     } else if (by == -VERYLARGE) {
  814.        /* note we know bx != -VERYLARGE */
  815.        *ex = ax;
  816.        *ey = min(y_min, y_max);
  817.        return;
  818.     }
  819.  
  820.     /* If we reach here, then either one point is (-VERYLARGE,-VERYLARGE), 
  821.     * or the inrange point is on the edge, and
  822.      * the line segment from the outrange point does not cross any 
  823.     * other edges to get there. In either case, we return the inrange 
  824.     * point as the 'edge' intersection point. This will basically draw
  825.     * line.
  826.     */
  827.     if (points[i].type == INRANGE) {
  828.        *ex = bx; 
  829.        *ey = by;
  830.     } else {
  831.        *ex = ax; 
  832.        *ey = ay;
  833.     }
  834.     return;
  835. }
  836.  
  837. /* double edge intersection algorithm */
  838. /* Given two points, both outside the plot, return
  839.  * the points where an edge of the plot intersects the line segment defined 
  840.  * by the two points. There may be zero, one, two, or an infinite number
  841.  * of intersection points. (One means an intersection at a corner, infinite
  842.  * means overlaying the edge itself). We return FALSE when there is nothing
  843.  * to draw (zero intersections), and TRUE when there is something to 
  844.  * draw (the one-point case is a degenerate of the two-point case and we do 
  845.  * not distinguish it - we draw it anyway).
  846.  */
  847. BOOLEAN                /* any intersection? */
  848. two_edge_intersect(points, i, lx, ly)
  849.     struct coordinate *points; /* the points array */
  850.     int i;                /* line segment from point i-1 to point i */
  851.     double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  852. {
  853.     /* global x_min, x_max, y_min, x_max */
  854.     double ax = points[i-1].x;
  855.     double ay = points[i-1].y;
  856.     double bx = points[i].x;
  857.     double by = points[i].y;
  858.     double x, y;            /* possible intersection point */
  859.     BOOLEAN intersect = FALSE;
  860.  
  861.     if (by == ay) {
  862.        /* horizontal line */
  863.        /* y coord must be in range, and line must span both x_min and x_max */
  864.        /* note that spanning x_min implies spanning x_max */
  865.        if (inrange(by, y_min, y_max) && inrange(x_min, ax, bx)) {
  866.           *lx++ = x_min;
  867.           *ly++ = by;
  868.           *lx++ = x_max;
  869.           *ly++ = by;
  870.           return(TRUE);
  871.        } else
  872.         return(FALSE);
  873.     } else if (bx == ax) {
  874.        /* vertical line */
  875.        /* x coord must be in range, and line must span both y_min and y_max */
  876.        /* note that spanning y_min implies spanning y_max */
  877.        if (inrange(bx, x_min, x_max) && inrange(y_min, ay, by)) {
  878.           *lx++ = bx;
  879.           *ly++ = y_min;
  880.           *lx++ = bx;
  881.           *ly++ = y_max;
  882.           return(TRUE);
  883.        } else
  884.         return(FALSE);
  885.     }
  886.  
  887.     /* slanted line of some kind */
  888.     /* there can be only zero or two intersections below */
  889.  
  890.     /* does it intersect y_min edge */
  891.     if (inrange(y_min, ay, by)) {
  892.        x = ax + (y_min-ay) * ((bx-ax) / (by-ay));
  893.        if (inrange(x, x_min, x_max)) {
  894.           *lx++ = x;
  895.           *ly++ = y_min;
  896.           intersect = TRUE;
  897.        }
  898.     }
  899.     
  900.     /* does it intersect y_max edge */
  901.     if (inrange(y_max, ay, by)) {
  902.        x = ax + (y_max-ay) * ((bx-ax) / (by-ay));
  903.        if (inrange(x, x_min, x_max)) {
  904.           *lx++ = x;
  905.           *ly++ = y_max;
  906.           intersect = TRUE;
  907.        }
  908.     }
  909.  
  910.     /* does it intersect x_min edge */
  911.     if (inrange(x_min, ax, bx)) {
  912.        y = ay + (x_min-ax) * ((by-ay) / (bx-ax));
  913.        if (inrange(y, y_min, y_max)) {
  914.           *lx++ = x_min;
  915.           *ly++ = y;
  916.           intersect = TRUE;
  917.        }
  918.     }
  919.  
  920.     /* does it intersect x_max edge */
  921.     if (inrange(x_max, ax, bx)) {
  922.        y = ay + (x_max-ax) * ((by-ay) / (bx-ax));
  923.        if (inrange(y, y_min, y_max)) {
  924.           *lx++ = x_max;
  925.           *ly++ = y;
  926.           intersect = TRUE;
  927.        }
  928.     }
  929.  
  930.     if (intersect)
  931.      return(TRUE);
  932.  
  933.     /* It is possible for one or more of the [ab][xy] values to be -VERYLARGE.
  934.     * If ax=bx=-VERYLARGE or ay=by=-VERYLARGE we have already returned
  935.     * FALSE above.
  936.     * Note that for ax=ay=-VERYLARGE or bx=by=-VERYLARGE we can do nothing.
  937.     * Otherwise we fall through all the tests above. 
  938.     * Handle them carefully here. As yet we have no way for them to be +VERYLARGE.
  939.     */
  940.     if (ax == -VERYLARGE) {
  941.        if (ay != -VERYLARGE
  942.           && inrange(by, y_min, y_max) && inrange(x_max, ax, bx)) {
  943.           *lx++ = x_min;
  944.           *ly = by;
  945.           *lx++ = x_max;
  946.           *ly = by;
  947.           intersect = TRUE;
  948.        }
  949.     } else if (bx == -VERYLARGE) {
  950.        if (by != -VERYLARGE
  951.           && inrange(ay, y_min, y_max) && inrange(x_max, ax, bx)) {
  952.           *lx++ = x_min;
  953.           *ly = ay;
  954.           *lx++ = x_max;
  955.           *ly = ay;
  956.           intersect = TRUE;
  957.        }
  958.     } else if (ay == -VERYLARGE) {
  959.        /* note we know ax != -VERYLARGE */
  960.        if (inrange(bx, x_min, x_max) && inrange(y_max, ay, by)) {
  961.           *lx++ = bx;
  962.           *ly = y_min;
  963.           *lx++ = bx;
  964.           *ly = y_max;
  965.           intersect = TRUE;
  966.        }
  967.     } else if (by == -VERYLARGE) {
  968.        /* note we know bx != -VERYLARGE */
  969.        if (inrange(ax, x_min, x_max) && inrange(y_max, ay, by)) {
  970.           *lx++ = ax;
  971.           *ly = y_min;
  972.           *lx++ = ax;
  973.           *ly = y_max;
  974.           intersect = TRUE;
  975.        }
  976.     }
  977.  
  978.     return(intersect);
  979. }
  980.  
  981. /* Polar transform of all curves */
  982. /* Original code by John Campbell (CAMPBELL@NAUVAX.bitnet) */
  983. polar_xform (plots, pcount)
  984.     struct curve_points *plots;
  985.     int pcount;            /* count of curves in plots array */
  986. {
  987.      struct curve_points *this_plot;
  988.      int curve;            /* loop var, for curves */
  989.      register int i, p_cnt;    /* loop/limit var, for points */
  990.      struct coordinate *pnts;    /* abbrev. for points array */
  991.     double x, y;            /* new cartesian value */
  992.     BOOLEAN anydefined = FALSE;
  993.  
  994. /*
  995.     Cycle through all the plots converting polar to rectangular.
  996.      If autoscaling, adjust max and mins. Ignore previous values.
  997.     If not autoscaling, use the yrange for both x and y ranges.
  998. */
  999.     if (autoscale_ly) {
  1000.         x_min = VERYLARGE;
  1001.         y_min = VERYLARGE;
  1002.         x_max = -VERYLARGE;
  1003.         y_max = -VERYLARGE;
  1004.         autoscale_lx = TRUE;
  1005.     } else {
  1006.         x_min = y_min;
  1007.         x_max = y_max;
  1008.     }
  1009.     
  1010.     this_plot = plots;
  1011.     for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
  1012.         p_cnt = this_plot->p_count;
  1013.         pnts = &(this_plot->points[0]);
  1014.  
  1015.     /*    Convert to cartesian all points in this curve. */
  1016.         for (i = 0; i < p_cnt; i++) {
  1017.             if (pnts[i].type != UNDEFINED) {
  1018.                  anydefined = TRUE;
  1019.                 x = pnts[i].y*cos(pnts[i].x);
  1020.                 y = pnts[i].y*sin(pnts[i].x);
  1021.                 pnts[i].x = x;
  1022.                 pnts[i].y = y;
  1023.                 if (autoscale_ly) {
  1024.                     if (x_min > x) x_min = x;
  1025.                     if (x_max < x) x_max = x;
  1026.                     if (y_min > y) y_min = y;
  1027.                     if (y_max < y) y_max = y;
  1028.                     pnts[i].type = INRANGE;
  1029.                 } else if(inrange(x, x_min, x_max) && inrange(y, y_min, y_max))
  1030.                   pnts[i].type = INRANGE;
  1031.                 else
  1032.                   pnts[i].type = OUTRANGE;
  1033.             }
  1034.         }    
  1035.     }
  1036.  
  1037.     if (autoscale_lx && anydefined && fabs(x_max - x_min) < zero) {
  1038.         /* This happens at least for the plot of 1/cos(x) (vertical line). */
  1039.         fprintf(stderr, "Warning: empty x range [%g:%g], ", x_min,x_max);
  1040.         if (x_min == 0.0) {
  1041.            x_min = -1; 
  1042.            x_max = 1;
  1043.         } else {
  1044.            x_min *= 0.9;
  1045.            x_max *= 1.1;
  1046.         }
  1047.         fprintf(stderr, "adjusting to [%g:%g]\n", x_min,x_max);
  1048.     }
  1049.     if (autoscale_ly && anydefined && fabs(y_max - y_min) < zero) {
  1050.         /* This happens at least for the plot of 1/sin(x) (horiz. line). */
  1051.         fprintf(stderr, "Warning: empty y range [%g:%g], ", y_min, y_max);
  1052.         if (y_min == 0.0) {
  1053.            y_min = -1;
  1054.            y_max = 1;
  1055.         } else {
  1056.            y_min *= 0.9;
  1057.            y_max *= 1.1;
  1058.         }
  1059.         fprintf(stderr, "adjusting to [%g:%g]\n", y_min, y_max);
  1060.     }
  1061. }
  1062.  
  1063. /* DRAW_YTICS: draw a regular tic series, y axis */
  1064. draw_ytics(start, incr, end)
  1065.         double start, incr, end; /* tic series definition */
  1066.         /* assume start < end, incr > 0 */
  1067. {
  1068.     double ticplace;
  1069.     int ltic;            /* for mini log tics */
  1070.     double lticplace;    /* for mini log tics */
  1071.     double ticmin, ticmax;    /* for checking if tic is almost inrange */
  1072.  
  1073.     if (end == VERYLARGE)            /* for user-def series */
  1074.         end = max(y_min,y_max);
  1075.  
  1076.     /* limit to right side of plot */
  1077.     end = min(end, max(y_min,y_max));
  1078.  
  1079.     /* to allow for rounding errors */
  1080.     ticmin = min(y_min,y_max) - SIGNIF*incr;
  1081.     ticmax = max(y_min,y_max) + SIGNIF*incr;
  1082.     end = end + SIGNIF*incr; 
  1083.  
  1084.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1085.         if ( inrange(ticplace,ticmin,ticmax) )
  1086.             ytick(ticplace, yformat, incr, 1.0);
  1087.         if (log_y && incr == 1.0) {
  1088.             /* add mini-ticks to log scale ticmarks */
  1089.             for (ltic = 2; ltic <= 9; ltic++) {
  1090.                 lticplace = ticplace+log10((double)ltic);
  1091.                 if ( inrange(lticplace,ticmin,ticmax) )
  1092.                     ytick(lticplace, "\0", incr, 0.5);
  1093.             }
  1094.         }
  1095.     }
  1096. }
  1097.  
  1098. /* DRAW_XTICS: draw a regular tic series, x axis */
  1099. draw_xtics(start, incr, end)
  1100.         double start, incr, end; /* tic series definition */
  1101.         /* assume start < end, incr > 0 */
  1102. {
  1103.     double ticplace;
  1104.     int ltic;            /* for mini log tics */
  1105.     double lticplace;    /* for mini log tics */
  1106.     double ticmin, ticmax;    /* for checking if tic is almost inrange */
  1107.  
  1108.     if (end == VERYLARGE)            /* for user-def series */
  1109.         end = max(x_min,x_max);
  1110.  
  1111.     /* limit to right side of plot */
  1112.     end = min(end, max(x_min,x_max));
  1113.  
  1114.     /* to allow for rounding errors */
  1115.     ticmin = min(x_min,x_max) - SIGNIF*incr;
  1116.     ticmax = max(x_min,x_max) + SIGNIF*incr;
  1117.     end = end + SIGNIF*incr; 
  1118.  
  1119.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1120.         if ( inrange(ticplace,ticmin,ticmax) )
  1121.             xtick(ticplace, xformat, incr, 1.0);
  1122.         if (log_x && incr == 1.0) {
  1123.             /* add mini-ticks to log scale ticmarks */
  1124.             for (ltic = 2; ltic <= 9; ltic++) {
  1125.                 lticplace = ticplace+log10((double)ltic);
  1126.                 if ( inrange(lticplace,ticmin,ticmax) )
  1127.                     xtick(lticplace, "\0", incr, 0.5);
  1128.             }
  1129.         }
  1130.     }
  1131. }
  1132.  
  1133. /* DRAW_SERIES_YTICS: draw a user tic series, y axis */
  1134. draw_series_ytics(start, incr, end)
  1135.         double start, incr, end; /* tic series definition */
  1136.         /* assume start < end, incr > 0 */
  1137. {
  1138.     double ticplace, place;
  1139.     double ticmin, ticmax;    /* for checking if tic is almost inrange */
  1140.     double spacing = log_y ? log10(incr) : incr;
  1141.  
  1142.     if (end == VERYLARGE)
  1143.         end = max(CheckLog(log_y, y_min), CheckLog(log_y, y_max));
  1144.     else
  1145.       /* limit to right side of plot */
  1146.       end = min(end, max(CheckLog(log_y, y_min), CheckLog(log_y, y_max)));
  1147.  
  1148.     /* to allow for rounding errors */
  1149.     ticmin = min(y_min,y_max) - SIGNIF*incr;
  1150.     ticmax = max(y_min,y_max) + SIGNIF*incr;
  1151.     end = end + SIGNIF*incr; 
  1152.  
  1153.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1154.         place = (log_y ? log10(ticplace) : ticplace);
  1155.         if ( inrange(place,ticmin,ticmax) )
  1156.          ytick(place, yformat, spacing, 1.0);
  1157.     }
  1158. }
  1159.  
  1160.  
  1161. /* DRAW_SERIES_XTICS: draw a user tic series, x axis */
  1162. draw_series_xtics(start, incr, end)
  1163.         double start, incr, end; /* tic series definition */
  1164.         /* assume start < end, incr > 0 */
  1165. {
  1166.     double ticplace, place;
  1167.     double ticmin, ticmax;    /* for checking if tic is almost inrange */
  1168.     double spacing = log_x ? log10(incr) : incr;
  1169.  
  1170.     if (end == VERYLARGE)
  1171.         end = max(CheckLog(log_x, x_min), CheckLog(log_x, x_max));
  1172.     else
  1173.       /* limit to right side of plot */
  1174.       end = min(end, max(CheckLog(log_x, x_min), CheckLog(log_x, x_max)));
  1175.  
  1176.     /* to allow for rounding errors */
  1177.     ticmin = min(x_min,x_max) - SIGNIF*incr;
  1178.     ticmax = max(x_min,x_max) + SIGNIF*incr;
  1179.     end = end + SIGNIF*incr; 
  1180.  
  1181.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1182.         place = (log_x ? log10(ticplace) : ticplace);
  1183.         if ( inrange(place,ticmin,ticmax) )
  1184.          xtick(place, xformat, spacing, 1.0);
  1185.     }
  1186. }
  1187.  
  1188. /* DRAW_SET_YTICS: draw a user tic set, y axis */
  1189. draw_set_ytics(list)
  1190.     struct ticmark *list;    /* list of tic marks */
  1191. {
  1192.     double ticplace;
  1193.     double incr = (y_max - y_min) / 10;
  1194.     /* global x_min, x_max, xscale, y_min, y_max, yscale */
  1195.  
  1196.     while (list != NULL) {
  1197.        ticplace = (log_y ? log10(list->position) : list->position);
  1198.        if ( inrange(ticplace, y_min, y_max)         /* in range */
  1199.           || NearlyEqual(ticplace, y_min, incr)    /* == y_min */
  1200.           || NearlyEqual(ticplace, y_max, incr))    /* == y_max */
  1201.         ytick(ticplace, list->label, incr, 1.0);
  1202.  
  1203.        list = list->next;
  1204.     }
  1205. }
  1206.  
  1207. /* DRAW_SET_XTICS: draw a user tic set, x axis */
  1208. draw_set_xtics(list)
  1209.     struct ticmark *list;    /* list of tic marks */
  1210. {
  1211.     double ticplace;
  1212.     double incr = (x_max - x_min) / 10;
  1213.     /* global x_min, x_max, xscale, y_min, y_max, yscale */
  1214.  
  1215.     while (list != NULL) {
  1216.        ticplace = (log_x ? log10(list->position) : list->position);
  1217.        if ( inrange(ticplace, x_min, x_max)         /* in range */
  1218.           || NearlyEqual(ticplace, x_min, incr)    /* == x_min */
  1219.           || NearlyEqual(ticplace, x_max, incr))    /* == x_max */
  1220.         xtick(ticplace, list->label, incr, 1.0);
  1221.  
  1222.        list = list->next;
  1223.     }
  1224. }
  1225.  
  1226. /* draw and label a y-axis ticmark */
  1227. ytick(place, text, spacing, ticscale)
  1228.         double place;                   /* where on axis to put it */
  1229.         char *text;                     /* optional text label */
  1230.         double spacing;         /* something to use with checkzero */
  1231.         float ticscale;         /* scale factor for tic mark (0..1] */
  1232. {
  1233.     register struct termentry *t = &term_tbl[term];
  1234.     char ticlabel[101];
  1235.     int ticsize = (int)((t->h_tic) * ticscale);
  1236.  
  1237.     place = CheckZero(place,spacing); /* to fix rounding error near zero */
  1238.     if (grid) {
  1239.            (*t->linetype)(-1);  /* axis line type */
  1240.            (*t->move)(xleft, map_y(place));
  1241.            (*t->vector)(xright, map_y(place));
  1242.            (*t->linetype)(-2); /* border linetype */
  1243.     }
  1244.     if (tic_in) {
  1245.            (*t->move)(xleft, map_y(place));
  1246.            (*t->vector)(xleft + ticsize, map_y(place));
  1247.            (*t->move)(xright, map_y(place));
  1248.            (*t->vector)(xright - ticsize, map_y(place));
  1249.     } else {
  1250.            (*t->move)(xleft, map_y(place));
  1251.            (*t->vector)(xleft - ticsize, map_y(place));
  1252.     }
  1253.  
  1254.     /* label the ticmark */
  1255.     if (text == NULL) 
  1256.      text = yformat;
  1257.     
  1258.     (void) sprintf(ticlabel, text, CheckLog(log_y, place));
  1259.     if ((*t->justify_text)(RIGHT)) {
  1260.        (*t->put_text)(xleft-(t->h_char),
  1261.                    map_y(place), ticlabel);
  1262.     } else {
  1263.        (*t->put_text)(xleft-(t->h_char)*(strlen(ticlabel)+1),
  1264.                    map_y(place), ticlabel);
  1265.     }
  1266. }
  1267.  
  1268. /* draw and label an x-axis ticmark */
  1269. xtick(place, text, spacing, ticscale)
  1270.         double place;                   /* where on axis to put it */
  1271.         char *text;                     /* optional text label */
  1272.         double spacing;         /* something to use with checkzero */
  1273.         float ticscale;         /* scale factor for tic mark (0..1] */
  1274. {
  1275.     register struct termentry *t = &term_tbl[term];
  1276.     char ticlabel[101];
  1277.     int ticsize = (int)((t->v_tic) * ticscale);
  1278.  
  1279.     place = CheckZero(place,spacing); /* to fix rounding error near zero */
  1280.     if (grid) {
  1281.            (*t->linetype)(-1);  /* axis line type */
  1282.            (*t->move)(map_x(place), ybot);
  1283.            (*t->vector)(map_x(place), ytop);
  1284.            (*t->linetype)(-2); /* border linetype */
  1285.     }
  1286.     if (tic_in) {
  1287.            (*t->move)(map_x(place), ybot);
  1288.            (*t->vector)(map_x(place), ybot + ticsize);
  1289.            (*t->move)(map_x(place), ytop);
  1290.            (*t->vector)(map_x(place), ytop - ticsize);
  1291.     } else {
  1292.            (*t->move)(map_x(place), ybot);
  1293.            (*t->vector)(map_x(place), ybot - ticsize);
  1294.     }
  1295.     
  1296.     /* label the ticmark */
  1297.     if (text == NULL)
  1298.      text = xformat;
  1299.  
  1300.     (void) sprintf(ticlabel, text, CheckLog(log_x, place));
  1301.     if ((*t->justify_text)(CENTRE)) {
  1302.        (*t->put_text)(map_x(place),
  1303.                    ybot-(t->v_char), ticlabel);
  1304.     } else {
  1305.        (*t->put_text)(map_x(place)-(t->h_char)*strlen(ticlabel)/2,
  1306.                    ybot-(t->v_char), ticlabel);
  1307.     }
  1308. }
  1309.