|
|
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;
}
}
}