|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T d
Length: 27046 (0x69a6) Types: TextFile Names: »dumpgrph.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─⟦this⟧ »EUUGD11/euug-87hel/sec1/graph/dumpgrph.c«
/* * 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; } } }