home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1986 Alan Kent
- *
- * Permission is granted to freely distribute part or
- * all of this code as long as it is not for profit
- * and this message is retained in the code.
- *
- * No resposibility is taken for any damage or incorect
- * results this program generates.
- *
- */
-
- /*
- * WARNING: This file is extremely sensative to changes.
- * Making one minor change may break other features.
- * This file probably needs to be re-written.
- */
-
- #include <stdio.h>
- #include <math.h>
- #include "graph.h"
- #include "y.tab.h"
-
-
- #define DEBUG 0
- #define DEBUG_LOG 0
-
-
- /* this info is for trying to place the text nicely on the screen */
- /* however, unfortunately it is different for all the different plotters! */
-
- #ifdef LASER
- #define CHAR_WIDTH 48
- #define CHAR_HEIGHT 100
- #else
- #define CHAR_WIDTH 80
- #define CHAR_HEIGHT 100
- #endif
-
-
- #define XSPACE 4096
- #define YSPACE 4096
- #define XORIGIN (15*CHAR_WIDTH)
- #define YORIGIN 650
- #define XRANGE (XSPACE-XORIGIN)
- #define YRANGE (YSPACE-YORIGIN-3*CHAR_HEIGHT)
- #define TICK_SIZE 50
- #define TEXT_GAP 400
-
- #define TICK_TEXT_GAP 400 /* minimum gap between ticks for log */
- #define TICK_GAP 40
-
- #define RAD (XSPACE/200)
-
- #define LEGEND_LINE_LENGTH 400
-
-
- extern double min_fun ();
- extern double max_fun ();
- extern double ceil ();
- extern double pow ();
- extern double log10 ();
-
-
- extern graph_st graph[];
- extern int num_graphs;
- extern char *graph_label;
- extern axis_st xaxis;
- extern axis_st yaxis;
- extern int horiz_legend;
- extern int vert_legend;
-
-
-
- static double lastx_clip; /* buffered coordinate for clip function */
- static double lasty_clip;
-
-
- dump_graphs ()
- {
- int i;
-
- if ( num_graphs < 1 ) {
- fprintf ( stderr , "No graphs to print\n" );
- exit ( 0 );
- }
-
- if ( xaxis.linear == LOGRITHMIC ) {
- for ( i = 0; i < num_graphs; i++ )
- log_tab ( graph[i].table , graph[i].table->next );
- }
-
- if ( yaxis.linear == LOGRITHMIC ) {
- for ( i = 0; i < num_graphs; i++ )
- log_tab ( graph[i].table->next , graph[i].table );
- }
-
- /* plot(3x) routines */
- openpl ();
- erase ();
- space ( 0 , 0 , XSPACE , YSPACE );
-
- if ( graph_label != NULL ) {
- move ( XORIGIN + XRANGE / 2 - strlen ( graph_label ) * CHAR_WIDTH / 2 ,
- YSPACE - CHAR_HEIGHT );
- label ( graph_label );
- }
-
- determine_range ( XAXIS , &xaxis );
- determine_range ( YAXIS , &yaxis );
-
- axes ();
-
- dump_legend ();
-
- for ( i = 0; i < num_graphs; i++ )
- draw_graph ( &graph[i] );
-
- /* plot(3x) */
- move ( 0 , 0 );
- closepl ();
- }
-
-
-
- /* discards values <= 0.0 */
-
- static
- log_tab ( table , othercol )
- table_st *table , *othercol;
- {
- int i , j;
- int warn_count;
-
- warn_count = 0;
- j = 0;
- for ( i = 0; i < table->size; i++ ) {
- if ( table->data[i] > 0.0 ) {
- othercol->data[j] = othercol->data[i];
- table->data[j] = log10 ( table->data[i] );
- j++;
- }
- else if ( warn_count++ == 0 )
- warn ( "negative or zero data to be plotted logrithmically discarded" );
- }
- table->size = j;
- othercol->size = j;
- }
-
-
-
- static
- draw_graph ( gptr )
- graph_st *gptr;
- {
- int i , size;
- int reseti;
- int basex , basey;
- double *xdata , *ydata;
- double lastx , lasty;
- double newx , newy;
-
-
- /* necessary for clip function to work safely */
-
- lasty_clip = -1;
- lastx_clip = -1;
-
- /* no data to plot */
-
- if ( gptr->table->size < 1 )
- return;
-
- xdata = gptr->table->data;
- ydata = gptr->table->next->data;
- size = gptr->table->size;
-
- /* draw the line (with clipping) */
-
- if ( gptr->line_type != NO ) {
- set_line ( gptr->line_type );
- lastx = xdata[0];
- lasty = ydata[0];
- /* start from 0 (not 1) so a single point will come out as a dot */
- for ( i = 0; i < size; i++ ) {
- newx = xdata[i];
- newy = ydata[i];
- clip_line ( lastx , lasty , newx , newy );
- lastx = newx;
- lasty = newy;
- }
- }
-
- /* Draw any points along the line */
-
- if ( gptr->point_type != 0 ) {
- set_line ( SOLID );
- for ( i = 0; i < size; i++ ) {
-
- basex = lxscale ( xdata[i] );
- basey = lyscale ( ydata[i] );
-
- if ( basex >= XORIGIN && basex <= XORIGIN + XRANGE
- && basey >= YORIGIN && basey <= YORIGIN + YRANGE )
-
- draw_point ( gptr->point_type , basex , basey );
- }
- }
-
- /* output the graph label */
-
- set_line ( SOLID );
- if ( gptr->label != NULL ) {
- move ( lxscale ( lastx_clip ) , lyscale ( lasty_clip ) );
- /*move ( lxscale ( xdata[size-1] ) , lyscale ( ydata[size-1] ) );*/
- label ( gptr->label );
- }
- }
-
-
-
- draw_point ( point_type , basex , basey )
- int point_type , basex , basey;
- {
- if ( point_type & MSK_TRIANGLE ) {
- move ( basex - RAD , basey - RAD * 2 / 3 );
- cont ( basex + RAD , basey - RAD * 2 / 3 );
- cont ( basex , basey + RAD * 4 / 3 );
- cont ( basex - RAD , basey - RAD * 2 / 3 );
- }
-
- if ( point_type & MSK_CROSS ) {
- move ( basex - RAD , basey - RAD );
- cont ( basex + RAD , basey + RAD );
- move ( basex - RAD , basey + RAD );
- cont ( basex + RAD , basey - RAD );
- }
-
- if ( point_type & MSK_PLUS ) {
- move ( basex - RAD , basey - RAD );
- cont ( basex + RAD , basey + RAD );
- move ( basex - RAD , basey + RAD );
- cont ( basex + RAD , basey - RAD );
- }
-
- if ( point_type & MSK_CIRCLE ) {
- circle ( basex , basey , RAD );
- }
-
- if ( point_type & MSK_SQUARE ) {
- /* *4/5 is just to make the shapes appear the same size */
- move ( basex - RAD * 4 / 5 , basey - RAD * 4 / 5 );
- cont ( basex + RAD * 4 / 5 , basey - RAD * 4 / 5 );
- cont ( basex + RAD * 4 / 5 , basey + RAD * 4 / 5 );
- cont ( basex - RAD * 4 / 5 , basey + RAD * 4 / 5 );
- cont ( basex - RAD * 4 / 5 , basey - RAD * 4 / 5 );
- }
- }
-
-
- clip_line ( x1 , y1 , x2 , y2 )
- double x1 , y1 , x2 , y2;
- {
- double minx , miny , maxx , maxy;
- int mx1 , my1 , mx2 , my2;
- double xedge , yedge;
-
-
- minx = xaxis.range.min;
- miny = yaxis.range.min;
- maxx = xaxis.range.max;
- maxy = yaxis.range.max;
-
- /* determine which sector the end points are in */
-
- mx1 = sector ( x1 , minx , maxx );
- my1 = sector ( y1 , miny , maxy );
- mx2 = sector ( x2 , minx , maxx );
- my2 = sector ( y2 , miny , maxy );
-
- /* points must not be in same sector and off screen */
-
- if ( mx1 * mx2 != 1 && my1 * my2 != 1 ) {
-
- if ( mx1 != 0 ) {
-
- /* move point 1 nearer the x-edge of the frame */
-
- if ( mx1 == 1 )
- xedge = maxx;
- else
- xedge = minx;
- y1 = y1 + ( y2 - y1 ) * ( xedge - x1 ) / ( x2 - x1 );
- x1 = xedge;
-
- /* recompute sector to see if point needs another shift */
-
- my1 = sector ( y1 , miny , maxy );
- }
-
- if ( my1 != 0 ) {
-
- /* move point 1 nearer the y-edge of the frame */
-
- if ( my1 == 1 )
- yedge = maxy;
- else
- yedge = miny;
- x1 = x1 + ( x2 - x1 ) * ( yedge - y1 ) / ( y2 - y1 );
- y1 = yedge;
-
- }
-
- /* ok, now repeat the above for point 2 */
-
- if ( mx2 != 0 ) {
-
- /* move point 2 nearer the x-edge of the frame */
-
- if ( mx2 == 1 )
- xedge = maxx;
- else
- xedge = minx;
- y2 = y2 + ( y1 - y2 ) * ( xedge - x2 ) / ( x1 - x2 );
- x2 = xedge;
-
- /* recompute sector to see if point needs another shift */
-
- my2 = sector ( y2 , miny , maxy );
- }
-
- if ( my2 != 0 ) {
-
- /* move point 2 nearer the y-edge of the frame */
-
- if ( my2 == 1 )
- yedge = maxy;
- else
- yedge = miny;
- x2 = x2 + ( x1 - x2 ) * ( yedge - y2 ) / ( y1 - y2 );
- y2 = yedge;
-
- }
-
- if ( x1 >= minx && x1 <= maxx && x2 >= minx && x2 <= maxx
- && y1 >= miny && y1 <= maxy && y2 >= miny && y2 <= maxy ) {
-
- /* use buffered move and cont calls where possible as some */
- /* plotters do dotted lines better if cont is used for continuous */
- /* lines. Using separate line() calls means the dotted lines */
- /* start again per line */
-
- if ( x1 != lastx_clip || y1 != lasty_clip )
- move ( xscale ( x1 ) , yscale ( y1 ) );
- cont ( xscale ( x2 ) , yscale ( y2 ) );
- lastx_clip = x2;
- lasty_clip = y2;
- }
- }
- }
-
-
- static int
- sector ( val , min , max )
- double val , min , max;
- {
- if ( val < min ) return ( -1 );
- if ( val > max ) return ( 1 );
- return ( 0 );
- }
-
-
- static
- set_line ( type )
- int type;
- {
- switch ( type ) {
- case DOTTED : linemod ( "dotted" ); break;
- case DOTDASHED : linemod ( "dotdashed" ); break;
- case SHORTDASHED : linemod ( "shortdashed" ); break;
- case LONGDASHED : linemod ( "longdashed" ); break;
- default : linemod ( "solid" ); break;
- }
- }
-
-
- static int
- xscale ( coord )
- double coord;
- {
- double xcoord;
-
- xcoord = ( ( coord - xaxis.range.min )
- / ( xaxis.range.max - xaxis.range.min ) ) * (double)XRANGE;
- return ( (int)xcoord + XORIGIN );
- }
-
-
- static int
- yscale ( coord )
- double coord;
- {
- double ycoord;
-
- ycoord = ( ( coord - yaxis.range.min )
- / ( yaxis.range.max - yaxis.range.min ) ) * (double)YRANGE;
- return ( (int)ycoord + YORIGIN );
- }
-
-
- static int
- lxscale ( coord )
- double coord;
- {
- int xcoord;
-
- xcoord = xscale ( coord );
- if ( xcoord < 0 )
- return ( 0 );
- if ( xcoord >= XSPACE )
- return ( XSPACE - 1 );
- return ( xcoord );
- }
-
-
- static int
- lyscale ( coord )
- double coord;
- {
- int ycoord;
-
- ycoord = yscale ( coord );
- if ( ycoord < 0 )
- return ( 0 );
- if ( ycoord >= YSPACE )
- return ( YSPACE - 1 );
- return ( ycoord );
- }
-
-
- char *
- choose_format ( paxis )
- struct axis_st *paxis;
- {
- double first_log_tick ();
-
- if ( paxis->linear == LOGRITHMIC ) {
- paxis->power_10 = floor ( paxis->range.min );
- paxis->format = "%.0f";
- /* STILL NOT RIGHT
- if ( xscale ( first_log_tick ( xaxis.range.min ) ) >= XRANGE + XORIGIN )
- paxis->format = "%.2f";
- */
- }
- else if ( paxis->auto_tick_size )
- calc_ticks ( paxis , &paxis->tick_size , &paxis->power_10 );
-
- if ( paxis->scale == AUTO
- /* && paxis->linear != LOGRITHMIC ???? special scaling ok for log too? */
- && paxis->auto_tick_size ) {
- if ( paxis->power_10 == 1.0
- || paxis->power_10 == 2.0 ) {
- paxis->format = "%.0f";
- paxis->scale = NO;
- }
- else if ( paxis->tick_size >= 10.0 )
- paxis->format = "%.0f";
- }
- return ( ( paxis->user_format == NULL )
- ? paxis->format : paxis->user_format );
- }
-
-
- static
- axes ()
- {
- int xscale ();
- int yscale ();
- int lxscale ();
- int lyscale ();
- double first_log_tick ();
- double next_log_tick ();
- double next_10_tick ();
-
- int base_x , base_y;
- int tick_pos;
- int last_tick_pos;
- int last_text_pos;
- int tick_num;
- int loop_count;
- int i;
- double tick_value;
- double base;
- double xaxis_min , yaxis_min;
- char buf[500];
- char small_buf[2];
- char *p , *beg;
- int longest , length , num_lines , line_num;
- int print_tick;
- int best_log_ticks;
- char *xformat , *yformat;
-
-
- xformat = choose_format ( &xaxis );
- yformat = choose_format ( &yaxis );
-
- base_x = lxscale ( xaxis.range.min );
- base_y = lyscale ( yaxis.range.min );
- base = 10.0;
- set_line ( SOLID );
-
- if ( xaxis.frame != NO ) {
-
- /* First the x-axis */
-
- /* draw base line */
-
- line ( base_x , base_y , base_x + XRANGE - 1 , base_y );
-
- if ( xaxis.frame == OUTLINE )
- line ( base_x , base_y + YRANGE - 1 , base_x + XRANGE - 1 , base_y + YRANGE - 1 );
-
- /* Label the axis */
-
- if ( xaxis.label != NULL ) {
- sprintf ( buf ,
- ( xaxis.scale != AUTO || xaxis.power_10 == 0.0 )
- ? "%s" : "%s x 10^%d" ,
- xaxis.label ,
- (int)xaxis.power_10 );
- move ( base_x + XRANGE / 2 - strlen ( buf ) * CHAR_WIDTH / 2 ,
- base_y - TEXT_GAP );
- label ( buf );
- /* fprintf(stderr,"xaxis label '%s'\n",buf);*/
- }
-
- /* Put ticks on the axis */
-
- /*fprintf(stderr,"power_10 = %f\n",xaxis.power_10);*/
- tick_num = 0;
- loop_count = 0;
- last_tick_pos = 0;
- last_text_pos = 0;
-
- if ( xaxis.linear == LOGRITHMIC ) {
- xaxis_min = xaxis.range.min;
-
- #if DEBUG
- fprintf(stderr,"min = %f\n",xaxis.range.min);
- #endif
-
- best_log_ticks = xscale ( first_log_tick ( xaxis.range.min ) )
- < XRANGE + XORIGIN;
- best_log_ticks = 1; /* override for now */
-
- #if DEBUG_LOG
- fprintf(stderr,"best = %d, xscale(min) = %d\n",best_log_ticks,xscale(first_log_tick(xaxis.range.min)));
- #endif
-
- }
- else {
-
- /* now, round the value off! If we dont do that, min */
- /* values that are strange fractions will cause misleading */
- /* values to be printed (%.1f will round fractions to one */
- /* decimal place which will be totally wrong for a tick size */
- /* of 0.1 */
-
- xaxis_min = ceil ( xaxis.range.min
- / ( xaxis.tick_size * pow ( base , xaxis.power_10 ) ) )
- * ( xaxis.tick_size * pow ( base , xaxis.power_10 ) );
- #if DEBUG
- fprintf(stderr,"x.min %f, x.tick_size %f, x.power_10 %f , xaxis_min %f\n",
- xaxis.range.min,xaxis.tick_size,xaxis.power_10,xaxis_min);
- #endif
- }
-
- while ( 1 ) {
-
- /* determine value to display next to tick */
-
- if ( xaxis.linear == LOGRITHMIC ) {
- if ( best_log_ticks ) {
- if ( loop_count == 0 ) {
- tick_value = first_log_tick ( xaxis.range.min );
- #if DEBUG_LOG
- fprintf(stderr,"min = %f, max = %f, tick = %f\n",xaxis.range.min,xaxis.range.max,tick_value);
- #endif
- }
- else
- tick_value = next_log_tick ();
- }
- else {
- tick_value = pow ( 10.0 , xaxis.range.min )
- + (double)tick_num
- * ( pow( 10.0 , xaxis.range.max )
- - pow ( 10.0 , xaxis.range.min ) ) / 5.0;
- #if DEBUG_LOG
- fprintf(stderr,"tick value is %f\n",tick_value);
- #endif
- }
- }
- else {
- tick_value = xaxis_min + (double)tick_num
- * xaxis.tick_size * pow ( base , xaxis.power_10 );
- }
- loop_count++;
-
- /* determine offset along axis to place tick */
-
- if ( xaxis.linear == LOGRITHMIC )
- tick_pos = xscale ( log10 ( tick_value ) );
- else
- tick_pos = xscale ( tick_value );
-
- /* see if meant to auto scale value */
-
- if ( xaxis.scale == AUTO )
- tick_value /= pow ( base , xaxis.power_10 );
- #if DEBUG_LOG
- fprintf(stderr,"scaled tickvalue to %f\n",tick_value);
- #endif
-
- /* run out of axis? */
-
- if ( tick_pos > XRANGE + XORIGIN )
- break;
-
- /* if logrithmic axis, must also check that ticks wont be */
- /* too close together */
-
- if ( xaxis.linear == LOGRITHMIC
- && tick_pos != xscale ( next_10_tick () - 1.0 ) ) {
- /* is tick too close to last tick? */
- if ( tick_pos < last_tick_pos + TICK_GAP ) {
- #if DEBUG_LOG
- fprintf(stderr,"tick %d (%d) too close to LAST position\n",tick_num,tick_pos);
- #endif
- continue; /* try next tick */
- }
- /* is tick too close to next 1,10,100,1000 value? */
- if ( tick_pos + TICK_GAP > xscale ( next_10_tick () ) ) {
- #if DEBUG_LOG
- fprintf(stderr,"tick %d (%d) too close to NEXT position\n",tick_num,tick_pos);
- fprintf(stderr,"next = %f, xscale(next) = %d\n",next_10_tick (),xscale(next_10_tick()));
- #endif
- continue; /* try next tick */
- }
- last_tick_pos = tick_pos;
- }
-
- /* draw the actual tick */
-
- if ( xaxis.frame == GRID )
- line ( tick_pos , base_y + YRANGE - 1 , tick_pos , base_y );
- else
- line ( tick_pos , base_y , tick_pos , base_y - TICK_SIZE );
-
- /* print the tick value */
-
- print_tick = 0;
- if ( xaxis.linear != LOGRITHMIC )
- print_tick = 1;
- else if ( tick_pos >= last_text_pos + TICK_TEXT_GAP ) {
- if ( xscale ( 2.0 ) - xscale ( 1.0 ) <= TICK_TEXT_GAP ) {
- if ( tick_pos == xscale ( next_10_tick () - 1.0 ) )
- print_tick = 1;
- }
- else {
- if ( tick_pos + TICK_TEXT_GAP <= xscale ( next_10_tick () ) )
- print_tick = 1;
- }
- }
- #if DEBUG|DEBUG_LOG
- fprintf(stderr,"tick_pos = %d, 2.0-1.0 = %d, print it %d\n",
- tick_pos,xscale(2.0)-xscale(1.0),print_tick);
- #endif
-
- if ( print_tick ) {
- sprintf ( buf , xformat , tick_value );
- move ( tick_pos - strlen ( buf ) * CHAR_WIDTH / 2 ,
- base_y - ( xaxis.frame == GRID ? 2 : 3 ) * TICK_SIZE );
- label ( buf );
- last_text_pos = tick_pos;
- #if DEBUG|DEBUG_LOG
- fprintf(stderr,"TICK %d at %d: '%s'\n",tick_num,tick_pos,buf);
- #endif
-
- }
-
- /* loop until tick position off axis */
-
- tick_num++;
- if ( tick_num > 100 )
- abort ( "axis tick algorithm failed!!" );
- }
- }
-
- /* Second, the y-axis */
-
- if ( yaxis.frame != NO ) {
-
- /* draw base line */
-
- line ( base_x , base_y , base_x , base_y + YRANGE - 1 );
-
- if ( yaxis.frame == OUTLINE )
- line ( base_x , base_y + YRANGE - 1 , base_x + XRANGE - 1 , base_y + YRANGE - 1 );
-
- /* Label the axis by writing values DOWN the axis */
-
- if ( yaxis.label != NULL ) {
- sprintf ( buf ,
- ( yaxis.scale != AUTO || yaxis.power_10 == 0.0 )
- ? "%s" : "%s x~10^%d" ,
- yaxis.label ,
- (int)yaxis.power_10 );
-
- #ifdef OLD_DOWN_AXIS
- for ( i = 0; buf[i] != '\0'; i++ ) {
- move ( base_x - TEXT_GAP - 2 * CHAR_WIDTH , base_y + YRANGE / 2
- + strlen ( buf ) * CHAR_HEIGHT / 2 - i * CHAR_HEIGHT );
- small_buf[0] = buf[i];
- small_buf[1] = '\0';
- label ( small_buf );
- }
- #else
-
- /* split label into many lines, each line separated by a space. */
- /* to allow a space to be force, ~ is mapped to a space at the */
- /* last moment. to place the text, each line is centred and */
- /* placed so that the longest line touches the edge of the */
- /* graph. */
-
- /* first find longest line and number of lines */
-
- longest = 0;
- num_lines = 0;
- p = buf;
- while ( *p != '\0' ) {
- num_lines++;
- length = 0;
- while ( *p != ' ' && *p != '\0' ) {
- p++;
- length++;
- }
- if ( *p == ' ' )
- p++;
- if ( length > longest )
- longest = length;
- }
-
- /* now print yaxis label by replacing blanks with \0 and */
- /* ~ with blanks and output each line */
-
- line_num = 0;
- p = buf;
- while ( *p != '\0' ) {
- beg = p;
- length = 0;
- while ( *p != '\0' && *p != ' ' ) {
- if ( *p == '~' )
- *p = ' ';
- length++;
- p++;
- }
- if ( *p == ' ' )
- *p++ = '\0';
- move ( ( longest * CHAR_WIDTH/2 ) - ( length * CHAR_WIDTH/2 ) ,
- base_y + YRANGE / 2 + num_lines * CHAR_HEIGHT
- - line_num * CHAR_HEIGHT * 3 / 2 );
- label ( beg );
- line_num++;
- }
-
- #endif
- }
-
- /* Put ticks on the axis */
-
- tick_num = 0;
- last_tick_pos = 0;
- last_text_pos = 0;
- loop_count = 0;
-
- if ( yaxis.linear == LOGRITHMIC ) {
- yaxis_min = yaxis.range.min;
- }
- else {
-
- /* now, round the value off! If we dont do that, min */
- /* values that are strange fractions will cause misleading */
- /* values to be printed (%.1f will round fractions to one */
- /* decimal place which will be totally wrong for a tick size */
- /* of 0.1 */
-
- yaxis_min = ceil ( yaxis.range.min
- / ( yaxis.tick_size * pow ( base , yaxis.power_10 ) ) )
- * ( yaxis.tick_size * pow ( base , yaxis.power_10 ) );
- }
-
- while ( 1 ) {
-
- /* determine value to display next to tick */
-
- if ( yaxis.linear == LOGRITHMIC ) {
- if ( loop_count == 0 )
- tick_value = first_log_tick ( yaxis.range.min );
- else
- tick_value = next_log_tick ();
- }
- else {
- tick_value = yaxis_min + (double)tick_num
- * yaxis.tick_size * pow ( base , yaxis.power_10 );
- }
- loop_count++;
-
- /* determine offset along axis to place tick */
-
- if ( yaxis.linear == LOGRITHMIC )
- tick_pos = yscale ( log10 ( tick_value ) );
- else
- tick_pos = yscale ( tick_value );
-
- /* see if meant to auto scale value */
-
- if ( yaxis.scale == AUTO )
- tick_value /= pow ( base , yaxis.power_10 );
-
- /* run out of axis? */
-
- if ( tick_pos > YRANGE + YORIGIN )
- break;
-
- /* if logrithmic axis, must also check that ticks wont be */
- /* too close together */
-
- if ( yaxis.linear == LOGRITHMIC
- && tick_pos != yscale ( next_10_tick () - 1.0 ) ) {
- /* is tick too close to last tick? */
- if ( tick_pos < last_tick_pos + TICK_GAP )
- continue; /* try next tick */
- /* is tick too close to next 1,10,100,1000 value? */
- if ( tick_pos + TICK_GAP > yscale ( next_10_tick () ) )
- continue; /* try next tick */
- last_tick_pos = tick_pos;
- }
-
- /* draw the actual tick */
-
- if ( yaxis.frame == GRID )
- line ( base_x + XRANGE - 1 , tick_pos , base_x , tick_pos );
- else
- line ( base_x , tick_pos , base_x - TICK_SIZE , tick_pos );
-
- /* print the tick value */
-
- print_tick = 0;
- if ( yaxis.linear != LOGRITHMIC )
- print_tick = 1;
- else if ( tick_pos >= last_text_pos + TICK_TEXT_GAP ) {
- if ( yscale ( 2.0 ) - yscale ( 1.0 ) <= TICK_TEXT_GAP ) {
- if ( tick_pos == yscale ( next_10_tick () - 1.0 ) )
- print_tick = 1;
- }
- else {
- if ( tick_pos + TICK_TEXT_GAP <= yscale ( next_10_tick () ) )
- print_tick = 1;
- }
- }
-
- if ( print_tick ) {
- sprintf ( buf , yformat , tick_value );
- move ( base_x - strlen ( buf ) * CHAR_WIDTH
- - ( yaxis.frame == GRID ? 1 : 2 ) * TICK_SIZE ,
- tick_pos - CHAR_WIDTH / 2 );
- label ( buf );
- last_text_pos = tick_pos;
- }
-
- /* loop until tick position off axis */
-
- tick_num++;
- if ( tick_num > 100 )
- abort ( "axis tick algorithm failed!!" );
- }
- }
- }
-
-
- static
- determine_range ( which , paxis )
- int which;
- axis_st *paxis;
- {
- double floor () , log10 () , pow ();
- int i;
- double best_min , best_max;
- double new_min , new_max;
- double round_max;
- table_st *table;
-
-
- /* only automatically calculate range if no range has been specified */
- /* (This is indicated by the min value being greater than the max value) */
-
- if ( paxis->range.min >= paxis->range.max ) {
-
- /* scan through all the graphs and determine the largest and */
- /* smallest value. */
-
- if ( which == XAXIS )
- table = graph[0].table;
- else
- table = graph[0].table->next;
- best_min = min_fun ( table , 0 , table->size - 1 );
- best_max = max_fun ( table , 0 , table->size - 1 );
- for ( i = 1; i < num_graphs; i++ ) {
- if ( which == XAXIS )
- table = graph[i].table;
- else
- table = graph[i].table->next;
- new_min = min_fun ( table , 0 , table->size - 1 );
- new_max = max_fun ( table , 0 , table->size - 1 );
- if ( new_min < best_min )
- best_min = new_min;
- if ( new_max > best_max )
- best_max = new_max;
- }
-
- /* check for zero height graph */
-
- if ( best_min == best_max ) {
-
- /* give the graph some height */
-
- if ( best_min == 0.0 ) {
- best_min = -1.0;
- best_max = 1.0;
- }
- else if ( best_min > 0.0 ) {
- if ( paxis->linear == LOGRITHMIC )
- best_min = best_min / 2.0;
- else
- best_min = 0;
- best_max = 2.0 * best_max;
- }
- else {
- best_min = best_min * 0.9;
- best_max = best_max * 1.1;
- }
- }
-
- /* make min a nice round value (eg: 4-1000 => 0-1000) */
-
- if ( best_max > 0.0 ) {
- round_max = pow ( (double)10.0 , floor ( log10 ( best_max / 3.0 ) ) );
- if ( round_max > 0.0 )
- best_min = floor ( best_min / round_max ) * round_max * 0.999;
- }
- /*
- if ( paxis->linear != LOGRITHMIC ) {
- if ( best_max > 0.0 ) {
- round_max = pow ( (double)10.0 , floor ( log10 ( best_max ) ) );
- best_min = floor ( best_min / round_max ) * round_max;
- }
- }
- else {
- if ( best_max > 1.0 ) {
- round_max = pow ( (double)10.0 , floor ( log10 ( best_max ) ) );
- best_min = floor ( best_min / round_max ) * round_max;
- }
- }
- */
- #if 0
- /* to stop 0->infinity graphs from hanging the program */
- if ( paxis->linear == LOGRITHMIC ) {
- if ( best_min < best_max / 5.0 )
- best_min = best_max / 5.0;
- /* remember, this is 10^5.0 */
- }
- #endif
- paxis->range.min = best_min;
- paxis->range.max = best_max;
- }
- else if ( paxis->linear == LOGRITHMIC ) {
-
- /* convert values to actual values */
-
- if ( paxis->range.min <= 0.0 )
- abort ( "Minimum value for axis on log graph is <= 0" );
- paxis->range.min = log10 ( paxis->range.min );
- paxis->range.max = log10 ( paxis->range.max );
- }
- }
-
-
-
- static
- calc_ticks ( paxis , tick , power )
- axis_st *paxis;
- double *tick , *power;
- {
- double log10 ();
- double base , delta;
- int power_of_10;
- double tick_size;
-
- base = 10.0;
- delta = paxis->range.max - paxis->range.min;
- power_of_10 = (int) floor ( log10 ( delta ) );
- tick_size = delta / pow ( base , (double)(power_of_10+1) );
- if ( tick_size <= 0.5 ) {
- tick_size = ceil ( (double)( tick_size * 10.0 ) ) / (double)10.0;
- }
- else {
- tick_size = 0.1;
- power_of_10++;
- }
-
- /* round tick size off to x10 ^3,6,9,12,.... which people understand */
- switch ( power_of_10 % 3 ) {
- case 0 : /* already mult of 3 */
- break;
- case 1 : /* its just too high (by one) */
- tick_size *= 10.0;
- power_of_10--;
- break;
- case 2 : /* its just too low (by one) */
- /*
- tick_size /= 10.0;
- power_of_10++;
- */
- tick_size *= 100.0;
- power_of_10 -= 2;
- break;
- }
-
- *tick = tick_size;
- *power = (double)power_of_10;
- }
-
-
-
- static int lt_value;
- static int lt_power;
-
-
- static double
- first_log_tick ( value )
- double value;
- {
- double actual;
- double ret_val;
-
- actual = pow ( (double)10.0 , value );
- lt_power = (int) floor ( log10 ( actual ) );
- lt_value = (int) ceil ( actual / pow ( (double)10.0 , (double)lt_power ) );
- while ( lt_value >= 10 ) {
- lt_value /= 10;
- lt_power++;
- }
- ret_val = (double)lt_value * pow ( (double)10.0 , (double)lt_power );
- /*fprintf(stderr,"first_log_tick: lt_power = %d, lt_value = %d, ret_val = %f\n",lt_power,lt_value,ret_val);*/
- return ( ret_val );
- }
-
-
- static double
- next_log_tick ()
- {
- lt_value++;
- while ( lt_value >= 10 ) {
- lt_value /= 10;
- lt_power++;
- }
- return ( (double)lt_value * pow ( (double)10.0 , (double)lt_power ) );
- }
-
-
- static double
- next_10_tick ()
- {
- return ( (double)( lt_power + 1 ) );
- }
-
-
- static
- dump_legend ()
- {
- int i;
- int num_lines;
- int longest;
- int basex , basey;
-
-
- /* first determine how much is going to be output, and what is the */
- /* longest string that is to be output */
-
- num_lines = 0;
- longest = 0;
- for ( i = 0; i < num_graphs; i++ ) {
- if ( graph[i].legend != NULL ) {
- num_lines++;
- if ( strlen ( graph[i].legend ) > longest )
- longest = strlen ( graph[i].legend );
- }
- }
-
- /* ok, now we want to work out where to start outputing the legend */
-
- switch ( horiz_legend ) {
-
- case LEFT :
- basex = XORIGIN + CHAR_WIDTH * 3;
- break;
-
- case CENTER :
- basex = XORIGIN + XRANGE / 2 - ( longest * CHAR_WIDTH - LEGEND_LINE_LENGTH ) / 2;
- break;
-
- case RIGHT :
- basex = XORIGIN + XRANGE - longest * CHAR_WIDTH - 3 * CHAR_WIDTH - LEGEND_LINE_LENGTH - CHAR_WIDTH;
- break;
- }
-
- switch ( vert_legend ) {
-
- case TOP :
- basey = YORIGIN + YRANGE - CHAR_HEIGHT * 2;
- break;
-
- case MIDDLE :
- basey = YORIGIN + YRANGE / 2 + num_lines * CHAR_HEIGHT / 2;
- break;
-
- case BOTTOM :
- basey = YORIGIN + CHAR_HEIGHT * 1 + num_lines * CHAR_HEIGHT;
- break;
- }
-
- /* ok, now loop through and acutally output the legend! */
-
- for ( i = 0; i < num_graphs; i++ ) {
- if ( graph[i].legend != NULL ) {
- if ( graph[i].line_type != NO ) {
- set_line ( graph[i].line_type );
- line ( basex , basey , basex + LEGEND_LINE_LENGTH , basey );
- }
- set_line ( SOLID );
- draw_point ( graph[i].point_type , basex + LEGEND_LINE_LENGTH / 2 , basey );
- move ( basex + LEGEND_LINE_LENGTH + CHAR_WIDTH , basey - CHAR_HEIGHT / 3 );
- label ( graph[i].legend );
- basey -= CHAR_HEIGHT;
- }
- }
- }
-