/*
 *  FILE: graph.c
 *  Support routines for creating and manipulating graphs.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Oct-88	Jan Sven Trabandt   added to gimme.lib
 */


#define I_AM_GRAPH
#include "gimmelib/gimmefuncs.h"
#include "gimmelib/graph.h"
#include "gimmelib/postext.h"
#include <clib/macros.h>


/* macros for internal scaling */
#define SCALEUP(x)  ((long)(x) << SCALE)        /* x * 2^SCALE */
#define SCALEDN(x)  ((long)(x) >> SCALE)        /* x / 2^SCALE */

/* macros to translate pixel offset from origin to pixel offset in rastport */
#define TRANSX(gr,off)  ((short)((gr)->X.origin + (off)))
#define TRANSY(gr,off)  ((short)((gr)->Y.origin - (off)))

/* leeways for titles */
#define XLEEWAY     4
#define YLEEWAY     3

#define TOLERANCE   2		/* pixel tolerance for fancy auto label */


/* internal flags for graphWriteLabel */
#define GWL_RESERVED_VAL    (0x0000ffffL)   /* lower short reserved for value */
#define GWL_NO_HIGH	    (1L << 16)      /* for internal use only!! */


/* forward declarations */
static SHORT coordToOffset();
static SHORT offsetToCoord();
static VOID  initAxis();
static SHORT updateCoord();
static VOID  doBlock();


GRAPH *gimmeGraph( newgr, bm, areainfo, tmpras )
    register NEWGRAPH	*newgr;
    struct BitMap	*bm;
    struct AreaInfo	*areainfo;
    struct TmpRas	*tmpras;
{
    register GRAPH  *gr;
    void	    *mymh = NULL;
    UBYTE	    *raster;
    SHORT	    width, height;

#ifdef GIMME_WIMPY
    if( !newgr ) {
	return( NULL );
    }
#endif
    if( !bm && (!newgr->rp || !newgr->rp->BitMap) ) {
	return( NULL );
    }
    gr = (GRAPH *) chainAllocMem( &mymh, (ULONG)sizeof(GRAPH),
				    MEMF_PUBLIC | MEMF_CLEAR );
    if( !gr ) {
	return( NULL );
    }
    if( !newgr->rp ) {
	InitRastPort( &gr->rp );
    } else {
	gr->rp = *newgr->rp;	    /* copy struct */
	if( newgr->rp->Layer ) {
	    gr->rp.Layer = chainAllocMem( &mymh, (ULONG)sizeof(struct Layer),
					    MEMF_PUBLIC | MEMF_CLEAR );
	    if( !gr->rp.Layer ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    *gr->rp.Layer = *newgr->rp->Layer;	/* copy struct */
	    gr->rp.Layer->rp = &gr->rp; 	/* make this layer come back */
	}
    }
    if( bm ) {
	gr->rp.BitMap = bm;
    }

    gr->title  = newgr->title;
    gr->xtitle = newgr->xtitle;
    gr->ytitle = newgr->ytitle;
    gr->FPen = newgr->FPen;
    gr->BPen = newgr->BPen;
    gr->AxesPen = newgr->AxesPen;
    gr->XlabPen = gr->YlabPen = gr->AxesPen;
    gr->TitlePen = newgr->TitlePen;
    gr->XtitlePen = gr->YtitlePen = gr->TitlePen;
    gr->flags = newgr->flags;
    gr->ctlflags = newgr->ctlflags;
    if( newgr->titleta ) {
	gr->titletf = gimmeFont( newgr->titleta );
    }
    if( newgr->xta ) {
	gr->xlabtf = gr->xtf = gimmeFont( newgr->xta );
    }
    if( newgr->yta ) {
	gr->ylabtf = gr->ytf = gimmeFont( newgr->yta );
    }

    if( !(gr->ctlflags & GGR_NOCLEARSTART) ) {
	SetRast( &gr->rp, (long) gr->BPen );
    }

    initAxis( gr, &gr->X, &newgr->X );
    initAxis( gr, &gr->Y, &newgr->Y );
    SetAPen( &gr->rp, (long) gr->FPen );
    SetBPen( &gr->rp, (long) gr->BPen );
    SetDrMd( &gr->rp, (ULONG) JAM1 );

    if( newgr->flags & (GGR_FILLTOX | GGR_FILLTOY) ) {
	if( areainfo ) {
	    gr->rp.AreaInfo = areainfo;
	} else if( !gr->rp.AreaInfo ) {
	    gr->rp.AreaInfo = chainAllocMem( &mymh,
			(ULONG)sizeof(struct AreaInfo) + 5 * 2 * sizeof(LONG),
			MEMF_PUBLIC | MEMF_CLEAR );
	    if( !gr->rp.AreaInfo ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    InitArea( gr->rp.AreaInfo, (SHORT *)(gr->rp.AreaInfo + 1), 5L );
	}
	if( tmpras ) {
	    gr->rp.TmpRas = tmpras;
	} else if( !gr->rp.TmpRas ) {
	    gr->rp.TmpRas = chainAllocMem( &mymh, (ULONG)sizeof(struct TmpRas),
					    MEMF_PUBLIC | MEMF_CLEAR );
	    if( !gr->rp.TmpRas ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    width = gr->rp.BitMap->BytesPerRow << 3;
	    height = gr->rp.BitMap->Rows;
	    raster = chainAllocMem( &mymh,
			(long)RASSIZE((long)width, (long)height),
			    MEMF_CHIP | MEMF_CLEAR );
	    if( !raster ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    InitTmpRas( gr->rp.TmpRas, raster,
			    (long)RASSIZE((long)width, (long)height) );
	}
    }

    if( !(gr->ctlflags & GGR_NOLABELS) ) {
	drawGraphAxes( gr );
    }

    if( gr->ctlflags & (GGR_INITPOINT | GGR_INITORIGIN) ) {
	gr->points = 1;
    } else {
	gr->points = 0;
    }
    gr->memhead = mymh;
    return( gr );
} /* gimmeGraph */


short getRidOfGraph( gr )
    GRAPH   *gr;
{
#ifdef GIMME_WIMPY
    if( !gr ) {
	return( -1 );
    }
#endif
    if( gr->ctlflags & GGR_CLEARONEND ) {
	clearGraph( gr );
    }
    if( gr->ytf ) {
	getRidOfFont( gr->ytf );
    }
    if( gr->xtf ) {
	getRidOfFont( gr->xtf );
    }
    if( gr->titletf ) {
	getRidOfFont( gr->titletf );
    }
    chainFreeMem( gr->memhead );
    return( 0 );
} /* getRidOfGraph */


VOID clearGraph( gr )
    register GRAPH  *gr;
{
    BYTE  fpen, bpen, axespen, titlepen;
    BYTE  xlabpen, ylabpen, xtitlepen, ytitlepen;

    if( !(gr->ctlflags & GGR_NOCLEARSTART) ) {
	SetRast( &gr->rp, (long) gr->BPen );
    } else {
	fpen = gr->FPen;
	bpen = gr->BPen;
	axespen = gr->AxesPen;
	titlepen = gr->TitlePen;
	xlabpen = gr->XlabPen;
	ylabpen = gr->YlabPen;
	xtitlepen = gr->XtitlePen;
	ytitlepen = gr->YtitlePen;
	gr->FPen = bpen;
	gr->AxesPen = bpen;
	gr->TitlePen = bpen;
	gr->XlabPen = bpen;
	gr->YlabPen = bpen;
	gr->XtitlePen = bpen;
	gr->YtitlePen = bpen;
	SetAPen( &gr->rp, (long) bpen );

	RectFill( &gr->rp, (long) TRANSX(gr,1), (long) TRANSY(gr,gr->Y.size),
		    (long) TRANSX(gr,gr->X.size), (long) TRANSY(gr,1) );
	if( !(gr->ctlflags & GGR_NOLABELS) ) {
	    drawGraphAxes( gr );
	}

	gr->FPen = fpen;
	gr->AxesPen = axespen;
	gr->TitlePen = titlepen;
	gr->XlabPen = xlabpen;
	gr->YlabPen = ylabpen;
	gr->XtitlePen = xtitlepen;
	gr->YtitlePen = ytitlepen;
    }
} /* clearGraph */


VOID resetGraph( gr )
    register GRAPH  *gr;
{
    clearGraph( gr );

    gr->X.low  = gr->X.flow;
    gr->X.high = gr->X.fhigh;
    gr->X.lastdata = gr->X.fdata;
    gr->X.lastoff  = gr->X.foff;

    gr->Y.low  = gr->Y.flow;
    gr->Y.high = gr->Y.fhigh;
    gr->Y.lastdata = gr->Y.fdata;
    gr->Y.lastoff  = gr->Y.foff;

    if( gr->ctlflags & (GGR_INITPOINT | GGR_INITORIGIN) ) {
	gr->points = 1;
    } else {
	gr->points = 0;
    }

    if( !(gr->ctlflags & GGR_NOLABELS) ) {
	drawGraphAxes( gr );
    }
} /* resetGraph */


VOID drawGraphAxesOnly( gr )
    register GRAPH  *gr;
{
    SHORT   maxrastlen, maxlen;

    SetAPen( &gr->rp, (long) gr->AxesPen );
    Move( &gr->rp, (long) TRANSX(gr,0), (long) TRANSY(gr,gr->Y.size) );
    Draw( &gr->rp, (long) TRANSX(gr,0), (long) TRANSY(gr,0) );
    Draw( &gr->rp, (long) TRANSX(gr,gr->X.size), (long) TRANSY(gr,0) );
    SetAPen( &gr->rp, (long) gr->FPen );
} /* drawGraphAxesOnly */


SHORT drawGraphAxes( gr )
    register GRAPH  *gr;
{
    SHORT   maxrastlen, maxlen;
    SHORT   yoff;
    ULONG   flags;

    drawGraphAxesOnly( gr );
    maxrastlen = graphWriteLabel( gr, GWL_XAXIS, gr->X.low, gr->X.high,
				    gr->X.step );
    maxlen = graphWriteLabel( gr, GWL_YAXIS, gr->Y.low, gr->Y.high,
				gr->Y.step );

    drawGraphTitle( gr, gr->X.size >> 1, gr->Y.size + YLEEWAY,
			GPT_XCENTRE | GPT_YBOTTOM );

    yoff = - (YLEEWAY << 1);
    if( !(gr->ctlflags & GGR_NOLABELS) ) {
	yoff -= (gr->xlabtf ? gr->xlabtf->tf_YSize : gr->rp.TxHeight);
    }
    drawGraphXtitle( gr, gr->X.size >> 1, yoff, GPT_XCENTRE | GPT_YTOP );

    flags = GPT_XCENTRE | GPT_YBOTTOM | GPT_YUPWARDS;
    if( (gr->ctlflags & GGR_HIRES) && !(gr->ctlflags & GGR_INTERLACE) ) {
	flags |= GPT_XTHICKEN;
    }
    drawGraphYtitle( gr, -maxlen - XLEEWAY*2, gr->Y.size >> 1, flags );

    if( maxlen > maxrastlen ) {
	maxrastlen = maxlen;
    }
    return( maxrastlen );
} /* drawGraphAxes */


VOID drawGraphTitle( gr, xoff, yoff, myflags )
    register GRAPH  *gr;
    SHORT	    xoff, yoff;
    ULONG	    myflags;
{
    struct TextFont *tf;

    if( gr->title ) {
	tf = gr->rp.Font;
	if( gr->titletf ) {
	    SetFont( &gr->rp, gr->titletf );
	}
	SetAPen( &gr->rp, (long) gr->TitlePen );
	positionText( &gr->rp, myflags, gr->title, 0L,
			TRANSX(gr,xoff), TRANSY(gr,yoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
	if( gr->titletf && tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
} /* drawGraphTitle */


VOID drawGraphXtitle( gr, xoff, yoff, myflags )
    register GRAPH  *gr;
    SHORT	    xoff, yoff;
    ULONG	    myflags;
{
    struct TextFont *tf;

    if( gr->xtitle ) {
	tf = gr->rp.Font;
	if( gr->xtf ) {
	    SetFont( &gr->rp, gr->xtf );
	}
	SetAPen( &gr->rp, (long) gr->XtitlePen );
	positionText( &gr->rp, myflags, gr->xtitle, 0L,
			TRANSX(gr,xoff), TRANSY(gr,yoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
	if( gr->xtf && tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
} /* drawGraphXtitle */


VOID drawGraphYtitle( gr, xoff, yoff, myflags )
    register GRAPH  *gr;
    SHORT	    xoff, yoff;
    ULONG	    myflags;
{
    SHORT   destx, desty;
    struct TextFont *tf;

    if( gr->ytitle ) {
	tf = gr->rp.Font;
	if( gr->ytf ) {
	    SetFont( &gr->rp, gr->ytf );
	}
	SetAPen( &gr->rp, (long) gr->YtitlePen );
	positionText( &gr->rp, myflags, gr->ytitle, 0L,
			TRANSX(gr,xoff), TRANSY(gr,yoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
	if( gr->ytf && tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
} /* drawGraphYtitle */


static SHORT coordToOffset( axis, coord )
    register AXIS   *axis;
    SHORT	    coord;
{
    if( axis->scale <= 0 ) {
	return( SCALEUP(coord - axis->low) / -axis->scale );
    } else {
	return( SCALEDN((coord - axis->low) * axis->scale) );
    }
} /* coordToOffset */


static SHORT offsetToCoord( axis, offset )
    register AXIS   *axis;
    SHORT	    offset;
{
    SHORT   coord;

    if( axis->scale <= 0 ) {
	coord = SCALEDN(offset * -axis->scale);
    } else {
	/* offset in "window" belongs to next higher coordinate */
	coord = (SCALEUP(offset) + axis->scale - 1) / axis->scale;
    }
    return( axis->low + coord );
} /* offsetToCoord */


/*  internal use only!!
 *  initialize a graph's axis given a new axis structure
static VOID initAxis( gr, axis, nax )
    GRAPH	    *gr;
    register AXIS   *axis;
    register NEWAXIS *nax;
{
    SHORT   temp;
    SHORT   amt;
    ULONG   flags;

    if( axis == &gr->X ) {
	flags = gr->flags & GGR_X_FLAGS;
    } else {
	flags = gr->flags & GGR_Y_FLAGS;
    }

    axis->origin = nax->origin;
    axis->size = nax->size;
    axis->flow = axis->low = nax->low;
    axis->step = nax->step;
    axis->labnum = nax->labnum;
    if( !nax->labdenom ) {              /* to avoid divide by zero!! */
	axis->labnum = 1;
	axis->labdenom = 1;
    } else {
	axis->labdenom = nax->labdenom;
    }
    amt = nax->amt;
    if( flags & (GGR_X_SPACING | GGR_Y_SPACING) ) {
	if( !amt ) {
	    ++amt;
	}
	axis->scale = SCALEUP(amt);
	temp = axis->size;
	do {
	    axis->high = offsetToCoord( axis, temp );
	    axis->usesize = coordToOffset( axis, axis->high );
	} while( axis->usesize > temp-- );
    } else {
	if( flags & (GGR_X_INTERVALS | GGR_Y_INTERVALS) ) {
	    if( amt <= 0 ) {
		amt = 5;
	    }
	    axis->high = axis->low + amt;
	} else { /* if( flags & (GGR_X_MAX | GGR_Y_MAX) ) */
	    if( amt < axis->low ) {
		amt = axis->low + 5;
	    }
	    axis->high = amt;
	}

	temp = axis->high - axis->low;
	if( temp >= nax->size ) {
	    axis->scale = - ( SCALEUP(temp) / axis->size );
	    temp = SCALE - 1;
	    while( (axis->usesize = coordToOffset(axis, axis->high))
		    > axis->size ) {
		if( temp < 0 ) break;
		axis->scale -= SCALEUP(5) >> temp;
		--temp;
	    } /* while */

	    if( flags & (GGR_X_INTEGRAL | GGR_DELTAX |
			    GGR_Y_INTEGRAL | GGR_DELTAY) ) {
		if( (temp = SCALEUP(SCALEDN(-axis->scale))) < -axis->scale ) {
		    axis->scale = -SCALEUP( SCALEDN(-axis->scale) + 1 );
		} else {
		    axis->scale = -temp;
		}
	    }
	} else {
	    axis->scale = SCALEUP(axis->size) / temp;
	    if( flags & (GGR_X_INTEGRAL | GGR_DELTAX |
			    GGR_Y_INTEGRAL | GGR_DELTAY) ) {
		axis->scale = SCALEUP( SCALEDN(axis->scale) );
	    }
	    axis->usesize = coordToOffset( axis, axis->high );
	}
    }

    axis->fhigh = axis->high;
    if( gr->ctlflags & GGR_INITPOINT ) {
	axis->lastdata = nax->initpt;
	axis->lastoff = coordToOffset( axis, axis->lastdata );
    } else {	/* if( gr->ctlflags & GGR_INITORIGIN ) */
	axis->lastdata = axis->low;
	axis->lastoff = 1;
    }
    axis->fdata = axis->lastdata;
    axis->foff = axis->lastoff;
    if( !axis->scale ) {
	axis->scale = 1;	    /* to avoid divide by zero!! */
    }
} /* initAxis */


SHORT graphWriteLabel( gr, myflags, first, last, step )
    register GRAPH  *gr;
    ULONG	    myflags;
    SHORT	    first, last;
    SHORT	    step;
{
    AXIS    *axis;
    SHORT   full, spacing;
    SHORT   low, high;
    SHORT   maxrlen = 0, len;
    SHORT   coord, loop;
    SHORT   x, y;
    struct TextFont *tf;
    BYTE    xpen, ypen;
    BYTE    bpen, dmode;

    if( !(myflags & GWL_NO_HIGH) ) {
	if( myflags & (GWL_CLEAR_OLD | GWL_CLEAR_ONLY) ) {
	    if( myflags & GWL_XAXIS ) {
		xpen = gr->XlabPen;
		gr->XlabPen = gr->BPen;
	    } else {
		ypen = gr->YlabPen;
		gr->YlabPen = gr->BPen;
	    }
	    maxrlen = graphWriteLabel( gr,
			    myflags & ~(GWL_CLEAR_OLD | GWL_CLEAR_ONLY),
			    first, last, step );
	    if( myflags & GWL_XAXIS ) {
		gr->XlabPen = xpen;
	    } else {
		gr->YlabPen = ypen;
	    }
	    if( myflags & GWL_CLEAR_ONLY ) {
		return( maxrlen );
	    }
	}
	tf = gr->rp.Font;
	bpen = gr->rp.BgPen;
	dmode = gr->rp.DrawMode;
	if( myflags & GWL_XAXIS ) {
	    SetAPen( &gr->rp, (long) gr->XlabPen );
	    if( gr->xlabtf ) {
		SetFont( &gr->rp, gr->xlabtf );
	    }
	} else {			/* else YAXIS */
	    SetAPen( &gr->rp, (long) gr->YlabPen );
	    if( gr->ylabtf ) {
		SetFont( &gr->rp, gr->ylabtf );
	    }
	}
    }

    if( step > 0 ) {                /* if we want even-spaced labels */
	for( loop = first; loop <= last; loop += step ) {
	    if( myflags & GWL_YAXIS) {
		y = coordToOffset(&gr->Y, loop);
		WritePixel( &gr->rp, (long)TRANSX(gr,-1), (long)TRANSY(gr,y) );
		len = positionText( &gr->rp, GPT_XRIGHT | GPT_YCENTREBASE, NULL,
				(long) (loop) * gr->Y.labnum / gr->Y.labdenom,
				TRANSX(gr,-XLEEWAY), TRANSY(gr,y) );
	    } else {	/* x-axis */
		x = coordToOffset(&gr->X, loop);
		WritePixel( &gr->rp, (long)TRANSX(gr,x), (long)TRANSY(gr,-1) );
		len = positionText( &gr->rp, GPT_XCENTRE | GPT_YTOP, NULL,
				(long) (loop) * gr->X.labnum / gr->X.labdenom,
				TRANSX(gr,x), TRANSY(gr,-YLEEWAY) );
	    }
	    maxrlen = MAX(maxrlen, len);
	} /* for */
    } else {
	if( myflags & GWL_YAXIS) {
	    high = coordToOffset(&gr->Y, last);
	    if( !(myflags & GWL_NO_HIGH) ) {
		WritePixel( &gr->rp, (long)TRANSX(gr,-1),
				(long)TRANSY(gr,high) );
		maxrlen = positionText(&gr->rp, GPT_XRIGHT | GPT_YCENTREBASE,
			    NULL, (long) (last) * gr->Y.labnum / gr->Y.labdenom,
			    TRANSX(gr,-XLEEWAY), TRANSY(gr,high) );
	    }
	    low = coordToOffset(&gr->Y, first);
	    WritePixel( &gr->rp, (long)TRANSX(gr,-1), (long)TRANSY(gr,low) );
	    positionText( &gr->rp, GPT_XRIGHT | GPT_YCENTREBASE, NULL,
			    (long) (first) * gr->Y.labnum / gr->Y.labdenom,
			    TRANSX(gr,-XLEEWAY), TRANSY(gr,low) );
	    full = gr->rp.TxHeight;
	    axis = &gr->Y;
	} else {	   /* x-axis */
	    high = coordToOffset(&gr->X, last);
	    if( !(myflags & GWL_NO_HIGH) ) {
		WritePixel( &gr->rp, (long)TRANSX(gr,high),
				(long)TRANSY(gr,-1) );
		maxrlen = positionText( &gr->rp, GPT_XCENTRE| GPT_YTOP, NULL,
				(long) (last) * gr->X.labnum / gr->X.labdenom,
				TRANSX(gr,high), TRANSY(gr,-YLEEWAY) );
		full = maxrlen;
		myflags = (myflags & ~0xFFFFL) | full;
	    } else {
		full = myflags & 0xFFFFL;
	    }
	    low = coordToOffset(&gr->X, first);
	    WritePixel( &gr->rp, (long)TRANSX(gr,low), (long)TRANSY(gr,-1) );
	    positionText( &gr->rp, GPT_XCENTRE | GPT_YTOP, NULL,
			    (long) (first) * gr->X.labnum / gr->X.labdenom,
			    TRANSX(gr,low), TRANSY(gr,-YLEEWAY) );
	    axis = &gr->X;
	}
	spacing = full << 1;
	low += spacing;
	coord = offsetToCoord( axis, low );
	low = coordToOffset( axis, coord );
	if( high - low >= spacing - TOLERANCE ) {
	    graphWriteLabel( gr, myflags | GWL_NO_HIGH, coord, last, step );
	}
    }

    if( !(myflags & GWL_NO_HIGH) ) {
	SetAPen( &gr->rp, (long) gr->FPen );
	if( tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
    return( maxrlen );
} /* graphWriteLabel */


VOID addToGraph( gr, x, y )
    register GRAPH  *gr;
    SHORT	    x, y;
{
    SHORT   amt;
    SHORT   newX, newY;

    newX = updateCoord( gr, &gr->X, x );
    newY = updateCoord( gr, &gr->Y, y );
    SetAPen( &gr->rp, (long) gr->FPen );
    if( !(gr->ctlflags & GGR_NOCONNECTLINE) ) {
	Move( &gr->rp, (long) TRANSX(gr,gr->X.lastoff),
				(long) TRANSY(gr,gr->Y.lastoff) );
	Draw( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
    } else {
	Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
    }

    if( gr->flags & GGR_LINETOX ) {
	Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
	Draw( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,1) );
    }
    if( gr->flags & GGR_LINETOY ) {
	Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
	Draw( &gr->rp, (long)TRANSX(gr,1), (long)TRANSY(gr,newY) );
    }

    if( gr->flags & GGR_FILLTOX ) {
	doBlock( &gr->rp, TRANSX(gr, gr->X.lastoff), TRANSY(gr, 1),
		    TRANSX(gr, gr->X.lastoff), TRANSY(gr, gr->Y.lastoff),
		    TRANSX(gr, newX), TRANSY(gr, newY),
		    TRANSX(gr, newX), TRANSY(gr, 1) );
    }
    if( gr->flags & GGR_FILLTOY ) {
	doBlock( &gr->rp, TRANSX(gr, 1), TRANSY(gr, gr->Y.lastoff),
		    TRANSX(gr,gr->X.lastoff), TRANSY(gr, gr->Y.lastoff),
		    TRANSX(gr, newX), TRANSY(gr, newY),
		    TRANSX(gr, 1), TRANSY(gr, newY) );
    }

    if( gr->flags & GGR_BLACKTOX ) {
	SetAPen( &gr->rp, (long) gr->BPen );
	Move( &gr->rp, (long) TRANSX(gr, gr->X.lastoff),(long)TRANSY(gr, 1) );
	Draw( &gr->rp, (long) TRANSX(gr, gr->X.lastoff),
			(long) TRANSY(gr, gr->Y.lastoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
    }
    if( gr->flags & GGR_BLACKTOY ) {
	SetAPen( &gr->rp, (long) gr->BPen );
	Move(&gr->rp, (long) TRANSX(gr, 1),(long)TRANSY(gr, gr->Y.lastoff) );
	Draw(&gr->rp, (long) TRANSX(gr, gr->X.lastoff),
			  (long) TRANSY(gr, gr->Y.lastoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
    }

    gr->X.lastoff = newX;
    gr->Y.lastoff = newY;
    ++gr->points;
} /* addToGraph */


/*  internal use only!!
 *  add a coordinate to an axis, doing the actual scrolling if necessary
 *  and updating some AXIS fields
 *
 *  note: truncates to the rectangle defined by the origin and the axis' size.
 *
 *  returns the standard pixel offset for the coordinate
static SHORT updateCoord( gr, axis, coord )
    GRAPH	    *gr;
    register AXIS   *axis;
    SHORT	    coord;
{
    SHORT   offset;
    SHORT   amt;
    LONG    Xamt, Yamt;
    ULONG   flags, awlflags;

    if( axis == &gr->X ) {
	flags = gr->flags & GGR_X_FLAGS;
    } else {
	flags = gr->flags & GGR_Y_FLAGS;
    }
    if( flags & (GGR_DELTAX | GGR_DELTAY) ) {
	axis->lastdata += coord;
	offset = coordToOffset( axis, axis->lastdata );
	if( offset > axis->usesize ) {
	    if( axis == &gr->X ) {
		Xamt = amt = offset - axis->lastoff;
		Yamt = 0L;
	    } else {
		Xamt = 0L;
		Yamt = amt = offset - axis->lastoff;
	    }
	    ScrollRaster( &gr->rp, (long) Xamt, (long) Yamt,
		(long) TRANSX(gr,1), (long) TRANSY(gr,gr->Y.size),
		(long) TRANSX(gr,axis->size), (long) TRANSY(gr,1) );
	    awlflags = (axis == &gr->X) ? GWL_XAXIS : GWL_YAXIS;
	    if( !(gr->ctlflags & GGR_NOLABELS) ) {
		graphWriteLabel( gr, awlflags | GWL_CLEAR_ONLY, axis->low,
				    axis->high, axis->step );
	    }
	    axis->low += coord;
	    axis->high += coord;
	    /* drawGraphAxesOnly( gr ); */
	    if( !(gr->ctlflags & GGR_NOLABELS) ) {
		graphWriteLabel( gr, awlflags, axis->low, axis->high,
					axis->step );
	    }
	    offset = axis->lastoff;
	    axis->lastoff -= amt;
	}
    } else {
	offset = coordToOffset( axis, coord );
	if( offset < 0 ) {
	    offset = 0;
	} else if( offset > axis->size ) {
	    offset = axis->size;
	}
	axis->lastdata = coord;
    }

    return( offset );
} /* updateCoord */


/*  internal use only!!
 *  do an area fill on the 4 pairs of coordinates,
 *  clipped to the rastport's bitmap size
 *  NOTE: the TmpRas should be (at least) the same size as this bitmap
static VOID doBlock( rp, x1, y1, x2, y2, x3, y3, x4, y4 )
    register struct RastPort	*rp;
    SHORT			x1, y1, x2, y2, x3, y3, x4, y4;
{
    SHORT   width, height;

    width = rp->BitMap->BytesPerRow << 3;
    height = rp->BitMap->Rows;

    if( x1 < 0 ) x1 = 0;
    else if( x1 > width ) x1 = width;
    if( x2 < 0 ) x2 = 0;
    else if( x2 > width ) x2 = width;
    if( x3 < 0 ) x3 = 0;
    else if( x3 > width ) x3 = width;
    if( x4 < 0 ) x4 = 0;
    else if( x4 > width ) x4 = width;

    if( x1 == x3 ) return;

    if( y1 < 0 ) y1 = 0;
    else if( y1 > height ) y1 = height;
    if( y2 < 0 ) y2 = 0;
    else if( y2 > height ) y2 = height;
    if( y3 < 0 ) y3 = 0;
    else if( y3 > height ) y3 = height;
    if( y4 < 0 ) y4 = 0;
    else if( y4 > height ) y4 = height;

    if( y1 == y3 ) return;

    AreaMove( rp, (long) x1, (long) y1 );
    AreaDraw( rp, (long) x2, (long) y2 );
    AreaDraw( rp, (long) x3, (long) y3 );
    AreaDraw( rp, (long) x4, (long) y4 );
    AreaEnd( rp );
} /* doBlock */
