home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 199.lha / GimmeLib / graph.c < prev    next >
C/C++ Source or Header  |  1988-12-27  |  21KB  |  800 lines

  1. /*
  2.  *  FILE: graph.c
  3.  *  Support routines for creating and manipulating graphs.
  4.  *
  5.  *  Public Domain, but keep my name in it as the original author.
  6.  *  31-Oct-88    Jan Sven Trabandt   added to gimme.lib
  7.  */
  8.  
  9.  
  10. #define I_AM_GRAPH
  11. #include "gimmelib/gimmefuncs.h"
  12. #include "gimmelib/graph.h"
  13. #include "gimmelib/postext.h"
  14. #include <clib/macros.h>
  15.  
  16.  
  17. /* macros for internal scaling */
  18. #define SCALEUP(x)  ((long)(x) << SCALE)        /* x * 2^SCALE */
  19. #define SCALEDN(x)  ((long)(x) >> SCALE)        /* x / 2^SCALE */
  20.  
  21. /* macros to translate pixel offset from origin to pixel offset in rastport */
  22. #define TRANSX(gr,off)  ((short)((gr)->X.origin + (off)))
  23. #define TRANSY(gr,off)  ((short)((gr)->Y.origin - (off)))
  24.  
  25. /* leeways for titles */
  26. #define XLEEWAY     4
  27. #define YLEEWAY     3
  28.  
  29. #define TOLERANCE   2        /* pixel tolerance for fancy auto label */
  30.  
  31.  
  32. /* internal flags for graphWriteLabel */
  33. #define GWL_RESERVED_VAL    (0x0000ffffL)   /* lower short reserved for value */
  34. #define GWL_NO_HIGH        (1L << 16)      /* for internal use only!! */
  35.  
  36.  
  37. /* forward declarations */
  38. static SHORT coordToOffset();
  39. static SHORT offsetToCoord();
  40. static VOID  initAxis();
  41. static SHORT updateCoord();
  42. static VOID  doBlock();
  43.  
  44.  
  45. GRAPH *gimmeGraph( newgr, bm, areainfo, tmpras )
  46.     register NEWGRAPH    *newgr;
  47.     struct BitMap    *bm;
  48.     struct AreaInfo    *areainfo;
  49.     struct TmpRas    *tmpras;
  50. {
  51.     register GRAPH  *gr;
  52.     void        *mymh = NULL;
  53.     UBYTE        *raster;
  54.     SHORT        width, height;
  55.  
  56. #ifdef GIMME_WIMPY
  57.     if( !newgr ) {
  58.     return( NULL );
  59.     }
  60. #endif
  61.     if( !bm && (!newgr->rp || !newgr->rp->BitMap) ) {
  62.     return( NULL );
  63.     }
  64.     gr = (GRAPH *) chainAllocMem( &mymh, (ULONG)sizeof(GRAPH),
  65.                     MEMF_PUBLIC | MEMF_CLEAR );
  66.     if( !gr ) {
  67.     return( NULL );
  68.     }
  69.     if( !newgr->rp ) {
  70.     InitRastPort( &gr->rp );
  71.     } else {
  72.     gr->rp = *newgr->rp;        /* copy struct */
  73.     if( newgr->rp->Layer ) {
  74.         gr->rp.Layer = chainAllocMem( &mymh, (ULONG)sizeof(struct Layer),
  75.                         MEMF_PUBLIC | MEMF_CLEAR );
  76.         if( !gr->rp.Layer ) {
  77.         chainFreeMem( mymh );
  78.         return( NULL );
  79.         }
  80.         *gr->rp.Layer = *newgr->rp->Layer;    /* copy struct */
  81.         gr->rp.Layer->rp = &gr->rp;     /* make this layer come back */
  82.     }
  83.     }
  84.     if( bm ) {
  85.     gr->rp.BitMap = bm;
  86.     }
  87.  
  88.     gr->title  = newgr->title;
  89.     gr->xtitle = newgr->xtitle;
  90.     gr->ytitle = newgr->ytitle;
  91.     gr->FPen = newgr->FPen;
  92.     gr->BPen = newgr->BPen;
  93.     gr->AxesPen = newgr->AxesPen;
  94.     gr->XlabPen = gr->YlabPen = gr->AxesPen;
  95.     gr->TitlePen = newgr->TitlePen;
  96.     gr->XtitlePen = gr->YtitlePen = gr->TitlePen;
  97.     gr->flags = newgr->flags;
  98.     gr->ctlflags = newgr->ctlflags;
  99.     if( newgr->titleta ) {
  100.     gr->titletf = gimmeFont( newgr->titleta );
  101.     }
  102.     if( newgr->xta ) {
  103.     gr->xlabtf = gr->xtf = gimmeFont( newgr->xta );
  104.     }
  105.     if( newgr->yta ) {
  106.     gr->ylabtf = gr->ytf = gimmeFont( newgr->yta );
  107.     }
  108.  
  109.     if( !(gr->ctlflags & GGR_NOCLEARSTART) ) {
  110.     SetRast( &gr->rp, (long) gr->BPen );
  111.     }
  112.  
  113.     initAxis( gr, &gr->X, &newgr->X );
  114.     initAxis( gr, &gr->Y, &newgr->Y );
  115.     SetAPen( &gr->rp, (long) gr->FPen );
  116.     SetBPen( &gr->rp, (long) gr->BPen );
  117.     SetDrMd( &gr->rp, (ULONG) JAM1 );
  118.  
  119.     if( newgr->flags & (GGR_FILLTOX | GGR_FILLTOY) ) {
  120.     if( areainfo ) {
  121.         gr->rp.AreaInfo = areainfo;
  122.     } else if( !gr->rp.AreaInfo ) {
  123.         gr->rp.AreaInfo = chainAllocMem( &mymh,
  124.             (ULONG)sizeof(struct AreaInfo) + 5 * 2 * sizeof(LONG),
  125.             MEMF_PUBLIC | MEMF_CLEAR );
  126.         if( !gr->rp.AreaInfo ) {
  127.         chainFreeMem( mymh );
  128.         return( NULL );
  129.         }
  130.         InitArea( gr->rp.AreaInfo, (SHORT *)(gr->rp.AreaInfo + 1), 5L );
  131.     }
  132.     if( tmpras ) {
  133.         gr->rp.TmpRas = tmpras;
  134.     } else if( !gr->rp.TmpRas ) {
  135.         gr->rp.TmpRas = chainAllocMem( &mymh, (ULONG)sizeof(struct TmpRas),
  136.                         MEMF_PUBLIC | MEMF_CLEAR );
  137.         if( !gr->rp.TmpRas ) {
  138.         chainFreeMem( mymh );
  139.         return( NULL );
  140.         }
  141.         width = gr->rp.BitMap->BytesPerRow << 3;
  142.         height = gr->rp.BitMap->Rows;
  143.         raster = chainAllocMem( &mymh,
  144.             (long)RASSIZE((long)width, (long)height),
  145.                 MEMF_CHIP | MEMF_CLEAR );
  146.         if( !raster ) {
  147.         chainFreeMem( mymh );
  148.         return( NULL );
  149.         }
  150.         InitTmpRas( gr->rp.TmpRas, raster,
  151.                 (long)RASSIZE((long)width, (long)height) );
  152.     }
  153.     }
  154.  
  155.     if( !(gr->ctlflags & GGR_NOLABELS) ) {
  156.     drawGraphAxes( gr );
  157.     }
  158.  
  159.     if( gr->ctlflags & (GGR_INITPOINT | GGR_INITORIGIN) ) {
  160.     gr->points = 1;
  161.     } else {
  162.     gr->points = 0;
  163.     }
  164.     gr->memhead = mymh;
  165.     return( gr );
  166. } /* gimmeGraph */
  167.  
  168.  
  169. short getRidOfGraph( gr )
  170.     GRAPH   *gr;
  171. {
  172. #ifdef GIMME_WIMPY
  173.     if( !gr ) {
  174.     return( -1 );
  175.     }
  176. #endif
  177.     if( gr->ctlflags & GGR_CLEARONEND ) {
  178.     clearGraph( gr );
  179.     }
  180.     if( gr->ytf ) {
  181.     getRidOfFont( gr->ytf );
  182.     }
  183.     if( gr->xtf ) {
  184.     getRidOfFont( gr->xtf );
  185.     }
  186.     if( gr->titletf ) {
  187.     getRidOfFont( gr->titletf );
  188.     }
  189.     chainFreeMem( gr->memhead );
  190.     return( 0 );
  191. } /* getRidOfGraph */
  192.  
  193.  
  194. VOID clearGraph( gr )
  195.     register GRAPH  *gr;
  196. {
  197.     BYTE  fpen, bpen, axespen, titlepen;
  198.     BYTE  xlabpen, ylabpen, xtitlepen, ytitlepen;
  199.  
  200.     if( !(gr->ctlflags & GGR_NOCLEARSTART) ) {
  201.     SetRast( &gr->rp, (long) gr->BPen );
  202.     } else {
  203.     fpen = gr->FPen;
  204.     bpen = gr->BPen;
  205.     axespen = gr->AxesPen;
  206.     titlepen = gr->TitlePen;
  207.     xlabpen = gr->XlabPen;
  208.     ylabpen = gr->YlabPen;
  209.     xtitlepen = gr->XtitlePen;
  210.     ytitlepen = gr->YtitlePen;
  211.     gr->FPen = bpen;
  212.     gr->AxesPen = bpen;
  213.     gr->TitlePen = bpen;
  214.     gr->XlabPen = bpen;
  215.     gr->YlabPen = bpen;
  216.     gr->XtitlePen = bpen;
  217.     gr->YtitlePen = bpen;
  218.     SetAPen( &gr->rp, (long) bpen );
  219.  
  220.     RectFill( &gr->rp, (long) TRANSX(gr,1), (long) TRANSY(gr,gr->Y.size),
  221.             (long) TRANSX(gr,gr->X.size), (long) TRANSY(gr,1) );
  222.     if( !(gr->ctlflags & GGR_NOLABELS) ) {
  223.         drawGraphAxes( gr );
  224.     }
  225.  
  226.     gr->FPen = fpen;
  227.     gr->AxesPen = axespen;
  228.     gr->TitlePen = titlepen;
  229.     gr->XlabPen = xlabpen;
  230.     gr->YlabPen = ylabpen;
  231.     gr->XtitlePen = xtitlepen;
  232.     gr->YtitlePen = ytitlepen;
  233.     }
  234. } /* clearGraph */
  235.  
  236.  
  237. VOID resetGraph( gr )
  238.     register GRAPH  *gr;
  239. {
  240.     clearGraph( gr );
  241.  
  242.     gr->X.low  = gr->X.flow;
  243.     gr->X.high = gr->X.fhigh;
  244.     gr->X.lastdata = gr->X.fdata;
  245.     gr->X.lastoff  = gr->X.foff;
  246.  
  247.     gr->Y.low  = gr->Y.flow;
  248.     gr->Y.high = gr->Y.fhigh;
  249.     gr->Y.lastdata = gr->Y.fdata;
  250.     gr->Y.lastoff  = gr->Y.foff;
  251.  
  252.     if( gr->ctlflags & (GGR_INITPOINT | GGR_INITORIGIN) ) {
  253.     gr->points = 1;
  254.     } else {
  255.     gr->points = 0;
  256.     }
  257.  
  258.     if( !(gr->ctlflags & GGR_NOLABELS) ) {
  259.     drawGraphAxes( gr );
  260.     }
  261. } /* resetGraph */
  262.  
  263.  
  264. VOID drawGraphAxesOnly( gr )
  265.     register GRAPH  *gr;
  266. {
  267.     SHORT   maxrastlen, maxlen;
  268.  
  269.     SetAPen( &gr->rp, (long) gr->AxesPen );
  270.     Move( &gr->rp, (long) TRANSX(gr,0), (long) TRANSY(gr,gr->Y.size) );
  271.     Draw( &gr->rp, (long) TRANSX(gr,0), (long) TRANSY(gr,0) );
  272.     Draw( &gr->rp, (long) TRANSX(gr,gr->X.size), (long) TRANSY(gr,0) );
  273.     SetAPen( &gr->rp, (long) gr->FPen );
  274. } /* drawGraphAxesOnly */
  275.  
  276.  
  277. SHORT drawGraphAxes( gr )
  278.     register GRAPH  *gr;
  279. {
  280.     SHORT   maxrastlen, maxlen;
  281.     SHORT   yoff;
  282.     ULONG   flags;
  283.  
  284.     drawGraphAxesOnly( gr );
  285.     maxrastlen = graphWriteLabel( gr, GWL_XAXIS, gr->X.low, gr->X.high,
  286.                     gr->X.step );
  287.     maxlen = graphWriteLabel( gr, GWL_YAXIS, gr->Y.low, gr->Y.high,
  288.                 gr->Y.step );
  289.  
  290.     drawGraphTitle( gr, gr->X.size >> 1, gr->Y.size + YLEEWAY,
  291.             GPT_XCENTRE | GPT_YBOTTOM );
  292.  
  293.     yoff = - (YLEEWAY << 1);
  294.     if( !(gr->ctlflags & GGR_NOLABELS) ) {
  295.     yoff -= (gr->xlabtf ? gr->xlabtf->tf_YSize : gr->rp.TxHeight);
  296.     }
  297.     drawGraphXtitle( gr, gr->X.size >> 1, yoff, GPT_XCENTRE | GPT_YTOP );
  298.  
  299.     flags = GPT_XCENTRE | GPT_YBOTTOM | GPT_YUPWARDS;
  300.     if( (gr->ctlflags & GGR_HIRES) && !(gr->ctlflags & GGR_INTERLACE) ) {
  301.     flags |= GPT_XTHICKEN;
  302.     }
  303.     drawGraphYtitle( gr, -maxlen - XLEEWAY*2, gr->Y.size >> 1, flags );
  304.  
  305.     if( maxlen > maxrastlen ) {
  306.     maxrastlen = maxlen;
  307.     }
  308.     return( maxrastlen );
  309. } /* drawGraphAxes */
  310.  
  311.  
  312. VOID drawGraphTitle( gr, xoff, yoff, myflags )
  313.     register GRAPH  *gr;
  314.     SHORT        xoff, yoff;
  315.     ULONG        myflags;
  316. {
  317.     struct TextFont *tf;
  318.  
  319.     if( gr->title ) {
  320.     tf = gr->rp.Font;
  321.     if( gr->titletf ) {
  322.         SetFont( &gr->rp, gr->titletf );
  323.     }
  324.     SetAPen( &gr->rp, (long) gr->TitlePen );
  325.     positionText( &gr->rp, myflags, gr->title, 0L,
  326.             TRANSX(gr,xoff), TRANSY(gr,yoff) );
  327.     SetAPen( &gr->rp, (long) gr->FPen );
  328.     if( gr->titletf && tf ) {
  329.         SetFont( &gr->rp, tf );
  330.     }
  331.     }
  332. } /* drawGraphTitle */
  333.  
  334.  
  335. VOID drawGraphXtitle( gr, xoff, yoff, myflags )
  336.     register GRAPH  *gr;
  337.     SHORT        xoff, yoff;
  338.     ULONG        myflags;
  339. {
  340.     struct TextFont *tf;
  341.  
  342.     if( gr->xtitle ) {
  343.     tf = gr->rp.Font;
  344.     if( gr->xtf ) {
  345.         SetFont( &gr->rp, gr->xtf );
  346.     }
  347.     SetAPen( &gr->rp, (long) gr->XtitlePen );
  348.     positionText( &gr->rp, myflags, gr->xtitle, 0L,
  349.             TRANSX(gr,xoff), TRANSY(gr,yoff) );
  350.     SetAPen( &gr->rp, (long) gr->FPen );
  351.     if( gr->xtf && tf ) {
  352.         SetFont( &gr->rp, tf );
  353.     }
  354.     }
  355. } /* drawGraphXtitle */
  356.  
  357.  
  358. VOID drawGraphYtitle( gr, xoff, yoff, myflags )
  359.     register GRAPH  *gr;
  360.     SHORT        xoff, yoff;
  361.     ULONG        myflags;
  362. {
  363.     SHORT   destx, desty;
  364.     struct TextFont *tf;
  365.  
  366.     if( gr->ytitle ) {
  367.     tf = gr->rp.Font;
  368.     if( gr->ytf ) {
  369.         SetFont( &gr->rp, gr->ytf );
  370.     }
  371.     SetAPen( &gr->rp, (long) gr->YtitlePen );
  372.     positionText( &gr->rp, myflags, gr->ytitle, 0L,
  373.             TRANSX(gr,xoff), TRANSY(gr,yoff) );
  374.     SetAPen( &gr->rp, (long) gr->FPen );
  375.     if( gr->ytf && tf ) {
  376.         SetFont( &gr->rp, tf );
  377.     }
  378.     }
  379. } /* drawGraphYtitle */
  380.  
  381.  
  382. static SHORT coordToOffset( axis, coord )
  383.     register AXIS   *axis;
  384.     SHORT        coord;
  385. {
  386.     if( axis->scale <= 0 ) {
  387.     return( SCALEUP(coord - axis->low) / -axis->scale );
  388.     } else {
  389.     return( SCALEDN((coord - axis->low) * axis->scale) );
  390.     }
  391. } /* coordToOffset */
  392.  
  393.  
  394. static SHORT offsetToCoord( axis, offset )
  395.     register AXIS   *axis;
  396.     SHORT        offset;
  397. {
  398.     SHORT   coord;
  399.  
  400.     if( axis->scale <= 0 ) {
  401.     coord = SCALEDN(offset * -axis->scale);
  402.     } else {
  403.     /* offset in "window" belongs to next higher coordinate */
  404.     coord = (SCALEUP(offset) + axis->scale - 1) / axis->scale;
  405.     }
  406.     return( axis->low + coord );
  407. } /* offsetToCoord */
  408.  
  409.  
  410. /*  internal use only!!
  411.  *  initialize a graph's axis given a new axis structure
  412. static VOID initAxis( gr, axis, nax )
  413.     GRAPH        *gr;
  414.     register AXIS   *axis;
  415.     register NEWAXIS *nax;
  416. {
  417.     SHORT   temp;
  418.     SHORT   amt;
  419.     ULONG   flags;
  420.  
  421.     if( axis == &gr->X ) {
  422.     flags = gr->flags & GGR_X_FLAGS;
  423.     } else {
  424.     flags = gr->flags & GGR_Y_FLAGS;
  425.     }
  426.  
  427.     axis->origin = nax->origin;
  428.     axis->size = nax->size;
  429.     axis->flow = axis->low = nax->low;
  430.     axis->step = nax->step;
  431.     axis->labnum = nax->labnum;
  432.     if( !nax->labdenom ) {              /* to avoid divide by zero!! */
  433.     axis->labnum = 1;
  434.     axis->labdenom = 1;
  435.     } else {
  436.     axis->labdenom = nax->labdenom;
  437.     }
  438.     amt = nax->amt;
  439.     if( flags & (GGR_X_SPACING | GGR_Y_SPACING) ) {
  440.     if( !amt ) {
  441.         ++amt;
  442.     }
  443.     axis->scale = SCALEUP(amt);
  444.     temp = axis->size;
  445.     do {
  446.         axis->high = offsetToCoord( axis, temp );
  447.         axis->usesize = coordToOffset( axis, axis->high );
  448.     } while( axis->usesize > temp-- );
  449.     } else {
  450.     if( flags & (GGR_X_INTERVALS | GGR_Y_INTERVALS) ) {
  451.         if( amt <= 0 ) {
  452.         amt = 5;
  453.         }
  454.         axis->high = axis->low + amt;
  455.     } else { /* if( flags & (GGR_X_MAX | GGR_Y_MAX) ) */
  456.         if( amt < axis->low ) {
  457.         amt = axis->low + 5;
  458.         }
  459.         axis->high = amt;
  460.     }
  461.  
  462.     temp = axis->high - axis->low;
  463.     if( temp >= nax->size ) {
  464.         axis->scale = - ( SCALEUP(temp) / axis->size );
  465.         temp = SCALE - 1;
  466.         while( (axis->usesize = coordToOffset(axis, axis->high))
  467.             > axis->size ) {
  468.         if( temp < 0 ) break;
  469.         axis->scale -= SCALEUP(5) >> temp;
  470.         --temp;
  471.         } /* while */
  472.  
  473.         if( flags & (GGR_X_INTEGRAL | GGR_DELTAX |
  474.                 GGR_Y_INTEGRAL | GGR_DELTAY) ) {
  475.         if( (temp = SCALEUP(SCALEDN(-axis->scale))) < -axis->scale ) {
  476.             axis->scale = -SCALEUP( SCALEDN(-axis->scale) + 1 );
  477.         } else {
  478.             axis->scale = -temp;
  479.         }
  480.         }
  481.     } else {
  482.         axis->scale = SCALEUP(axis->size) / temp;
  483.         if( flags & (GGR_X_INTEGRAL | GGR_DELTAX |
  484.                 GGR_Y_INTEGRAL | GGR_DELTAY) ) {
  485.         axis->scale = SCALEUP( SCALEDN(axis->scale) );
  486.         }
  487.         axis->usesize = coordToOffset( axis, axis->high );
  488.     }
  489.     }
  490.  
  491.     axis->fhigh = axis->high;
  492.     if( gr->ctlflags & GGR_INITPOINT ) {
  493.     axis->lastdata = nax->initpt;
  494.     axis->lastoff = coordToOffset( axis, axis->lastdata );
  495.     } else {    /* if( gr->ctlflags & GGR_INITORIGIN ) */
  496.     axis->lastdata = axis->low;
  497.     axis->lastoff = 1;
  498.     }
  499.     axis->fdata = axis->lastdata;
  500.     axis->foff = axis->lastoff;
  501.     if( !axis->scale ) {
  502.     axis->scale = 1;        /* to avoid divide by zero!! */
  503.     }
  504. } /* initAxis */
  505.  
  506.  
  507. SHORT graphWriteLabel( gr, myflags, first, last, step )
  508.     register GRAPH  *gr;
  509.     ULONG        myflags;
  510.     SHORT        first, last;
  511.     SHORT        step;
  512. {
  513.     AXIS    *axis;
  514.     SHORT   full, spacing;
  515.     SHORT   low, high;
  516.     SHORT   maxrlen = 0, len;
  517.     SHORT   coord, loop;
  518.     SHORT   x, y;
  519.     struct TextFont *tf;
  520.     BYTE    xpen, ypen;
  521.     BYTE    bpen, dmode;
  522.  
  523.     if( !(myflags & GWL_NO_HIGH) ) {
  524.     if( myflags & (GWL_CLEAR_OLD | GWL_CLEAR_ONLY) ) {
  525.         if( myflags & GWL_XAXIS ) {
  526.         xpen = gr->XlabPen;
  527.         gr->XlabPen = gr->BPen;
  528.         } else {
  529.         ypen = gr->YlabPen;
  530.         gr->YlabPen = gr->BPen;
  531.         }
  532.         maxrlen = graphWriteLabel( gr,
  533.                 myflags & ~(GWL_CLEAR_OLD | GWL_CLEAR_ONLY),
  534.                 first, last, step );
  535.         if( myflags & GWL_XAXIS ) {
  536.         gr->XlabPen = xpen;
  537.         } else {
  538.         gr->YlabPen = ypen;
  539.         }
  540.         if( myflags & GWL_CLEAR_ONLY ) {
  541.         return( maxrlen );
  542.         }
  543.     }
  544.     tf = gr->rp.Font;
  545.     bpen = gr->rp.BgPen;
  546.     dmode = gr->rp.DrawMode;
  547.     if( myflags & GWL_XAXIS ) {
  548.         SetAPen( &gr->rp, (long) gr->XlabPen );
  549.         if( gr->xlabtf ) {
  550.         SetFont( &gr->rp, gr->xlabtf );
  551.         }
  552.     } else {            /* else YAXIS */
  553.         SetAPen( &gr->rp, (long) gr->YlabPen );
  554.         if( gr->ylabtf ) {
  555.         SetFont( &gr->rp, gr->ylabtf );
  556.         }
  557.     }
  558.     }
  559.  
  560.     if( step > 0 ) {                /* if we want even-spaced labels */
  561.     for( loop = first; loop <= last; loop += step ) {
  562.         if( myflags & GWL_YAXIS) {
  563.         y = coordToOffset(&gr->Y, loop);
  564.         WritePixel( &gr->rp, (long)TRANSX(gr,-1), (long)TRANSY(gr,y) );
  565.         len = positionText( &gr->rp, GPT_XRIGHT | GPT_YCENTREBASE, NULL,
  566.                 (long) (loop) * gr->Y.labnum / gr->Y.labdenom,
  567.                 TRANSX(gr,-XLEEWAY), TRANSY(gr,y) );
  568.         } else {    /* x-axis */
  569.         x = coordToOffset(&gr->X, loop);
  570.         WritePixel( &gr->rp, (long)TRANSX(gr,x), (long)TRANSY(gr,-1) );
  571.         len = positionText( &gr->rp, GPT_XCENTRE | GPT_YTOP, NULL,
  572.                 (long) (loop) * gr->X.labnum / gr->X.labdenom,
  573.                 TRANSX(gr,x), TRANSY(gr,-YLEEWAY) );
  574.         }
  575.         maxrlen = MAX(maxrlen, len);
  576.     } /* for */
  577.     } else {
  578.     if( myflags & GWL_YAXIS) {
  579.         high = coordToOffset(&gr->Y, last);
  580.         if( !(myflags & GWL_NO_HIGH) ) {
  581.         WritePixel( &gr->rp, (long)TRANSX(gr,-1),
  582.                 (long)TRANSY(gr,high) );
  583.         maxrlen = positionText(&gr->rp, GPT_XRIGHT | GPT_YCENTREBASE,
  584.                 NULL, (long) (last) * gr->Y.labnum / gr->Y.labdenom,
  585.                 TRANSX(gr,-XLEEWAY), TRANSY(gr,high) );
  586.         }
  587.         low = coordToOffset(&gr->Y, first);
  588.         WritePixel( &gr->rp, (long)TRANSX(gr,-1), (long)TRANSY(gr,low) );
  589.         positionText( &gr->rp, GPT_XRIGHT | GPT_YCENTREBASE, NULL,
  590.                 (long) (first) * gr->Y.labnum / gr->Y.labdenom,
  591.                 TRANSX(gr,-XLEEWAY), TRANSY(gr,low) );
  592.         full = gr->rp.TxHeight;
  593.         axis = &gr->Y;
  594.     } else {       /* x-axis */
  595.         high = coordToOffset(&gr->X, last);
  596.         if( !(myflags & GWL_NO_HIGH) ) {
  597.         WritePixel( &gr->rp, (long)TRANSX(gr,high),
  598.                 (long)TRANSY(gr,-1) );
  599.         maxrlen = positionText( &gr->rp, GPT_XCENTRE| GPT_YTOP, NULL,
  600.                 (long) (last) * gr->X.labnum / gr->X.labdenom,
  601.                 TRANSX(gr,high), TRANSY(gr,-YLEEWAY) );
  602.         full = maxrlen;
  603.         myflags = (myflags & ~0xFFFFL) | full;
  604.         } else {
  605.         full = myflags & 0xFFFFL;
  606.         }
  607.         low = coordToOffset(&gr->X, first);
  608.         WritePixel( &gr->rp, (long)TRANSX(gr,low), (long)TRANSY(gr,-1) );
  609.         positionText( &gr->rp, GPT_XCENTRE | GPT_YTOP, NULL,
  610.                 (long) (first) * gr->X.labnum / gr->X.labdenom,
  611.                 TRANSX(gr,low), TRANSY(gr,-YLEEWAY) );
  612.         axis = &gr->X;
  613.     }
  614.     spacing = full << 1;
  615.     low += spacing;
  616.     coord = offsetToCoord( axis, low );
  617.     low = coordToOffset( axis, coord );
  618.     if( high - low >= spacing - TOLERANCE ) {
  619.         graphWriteLabel( gr, myflags | GWL_NO_HIGH, coord, last, step );
  620.     }
  621.     }
  622.  
  623.     if( !(myflags & GWL_NO_HIGH) ) {
  624.     SetAPen( &gr->rp, (long) gr->FPen );
  625.     if( tf ) {
  626.         SetFont( &gr->rp, tf );
  627.     }
  628.     }
  629.     return( maxrlen );
  630. } /* graphWriteLabel */
  631.  
  632.  
  633. VOID addToGraph( gr, x, y )
  634.     register GRAPH  *gr;
  635.     SHORT        x, y;
  636. {
  637.     SHORT   amt;
  638.     SHORT   newX, newY;
  639.  
  640.     newX = updateCoord( gr, &gr->X, x );
  641.     newY = updateCoord( gr, &gr->Y, y );
  642.     SetAPen( &gr->rp, (long) gr->FPen );
  643.     if( !(gr->ctlflags & GGR_NOCONNECTLINE) ) {
  644.     Move( &gr->rp, (long) TRANSX(gr,gr->X.lastoff),
  645.                 (long) TRANSY(gr,gr->Y.lastoff) );
  646.     Draw( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
  647.     } else {
  648.     Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
  649.     }
  650.  
  651.     if( gr->flags & GGR_LINETOX ) {
  652.     Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
  653.     Draw( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,1) );
  654.     }
  655.     if( gr->flags & GGR_LINETOY ) {
  656.     Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
  657.     Draw( &gr->rp, (long)TRANSX(gr,1), (long)TRANSY(gr,newY) );
  658.     }
  659.  
  660.     if( gr->flags & GGR_FILLTOX ) {
  661.     doBlock( &gr->rp, TRANSX(gr, gr->X.lastoff), TRANSY(gr, 1),
  662.             TRANSX(gr, gr->X.lastoff), TRANSY(gr, gr->Y.lastoff),
  663.             TRANSX(gr, newX), TRANSY(gr, newY),
  664.             TRANSX(gr, newX), TRANSY(gr, 1) );
  665.     }
  666.     if( gr->flags & GGR_FILLTOY ) {
  667.     doBlock( &gr->rp, TRANSX(gr, 1), TRANSY(gr, gr->Y.lastoff),
  668.             TRANSX(gr,gr->X.lastoff), TRANSY(gr, gr->Y.lastoff),
  669.             TRANSX(gr, newX), TRANSY(gr, newY),
  670.             TRANSX(gr, 1), TRANSY(gr, newY) );
  671.     }
  672.  
  673.     if( gr->flags & GGR_BLACKTOX ) {
  674.     SetAPen( &gr->rp, (long) gr->BPen );
  675.     Move( &gr->rp, (long) TRANSX(gr, gr->X.lastoff),(long)TRANSY(gr, 1) );
  676.     Draw( &gr->rp, (long) TRANSX(gr, gr->X.lastoff),
  677.             (long) TRANSY(gr, gr->Y.lastoff) );
  678.     SetAPen( &gr->rp, (long) gr->FPen );
  679.     }
  680.     if( gr->flags & GGR_BLACKTOY ) {
  681.     SetAPen( &gr->rp, (long) gr->BPen );
  682.     Move(&gr->rp, (long) TRANSX(gr, 1),(long)TRANSY(gr, gr->Y.lastoff) );
  683.     Draw(&gr->rp, (long) TRANSX(gr, gr->X.lastoff),
  684.               (long) TRANSY(gr, gr->Y.lastoff) );
  685.     SetAPen( &gr->rp, (long) gr->FPen );
  686.     }
  687.  
  688.     gr->X.lastoff = newX;
  689.     gr->Y.lastoff = newY;
  690.     ++gr->points;
  691. } /* addToGraph */
  692.  
  693.  
  694. /*  internal use only!!
  695.  *  add a coordinate to an axis, doing the actual scrolling if necessary
  696.  *  and updating some AXIS fields
  697.  *
  698.  *  note: truncates to the rectangle defined by the origin and the axis' size.
  699.  *
  700.  *  returns the standard pixel offset for the coordinate
  701. static SHORT updateCoord( gr, axis, coord )
  702.     GRAPH        *gr;
  703.     register AXIS   *axis;
  704.     SHORT        coord;
  705. {
  706.     SHORT   offset;
  707.     SHORT   amt;
  708.     LONG    Xamt, Yamt;
  709.     ULONG   flags, awlflags;
  710.  
  711.     if( axis == &gr->X ) {
  712.     flags = gr->flags & GGR_X_FLAGS;
  713.     } else {
  714.     flags = gr->flags & GGR_Y_FLAGS;
  715.     }
  716.     if( flags & (GGR_DELTAX | GGR_DELTAY) ) {
  717.     axis->lastdata += coord;
  718.     offset = coordToOffset( axis, axis->lastdata );
  719.     if( offset > axis->usesize ) {
  720.         if( axis == &gr->X ) {
  721.         Xamt = amt = offset - axis->lastoff;
  722.         Yamt = 0L;
  723.         } else {
  724.         Xamt = 0L;
  725.         Yamt = amt = offset - axis->lastoff;
  726.         }
  727.         ScrollRaster( &gr->rp, (long) Xamt, (long) Yamt,
  728.         (long) TRANSX(gr,1), (long) TRANSY(gr,gr->Y.size),
  729.         (long) TRANSX(gr,axis->size), (long) TRANSY(gr,1) );
  730.         awlflags = (axis == &gr->X) ? GWL_XAXIS : GWL_YAXIS;
  731.         if( !(gr->ctlflags & GGR_NOLABELS) ) {
  732.         graphWriteLabel( gr, awlflags | GWL_CLEAR_ONLY, axis->low,
  733.                     axis->high, axis->step );
  734.         }
  735.         axis->low += coord;
  736.         axis->high += coord;
  737.         /* drawGraphAxesOnly( gr ); */
  738.         if( !(gr->ctlflags & GGR_NOLABELS) ) {
  739.         graphWriteLabel( gr, awlflags, axis->low, axis->high,
  740.                     axis->step );
  741.         }
  742.         offset = axis->lastoff;
  743.         axis->lastoff -= amt;
  744.     }
  745.     } else {
  746.     offset = coordToOffset( axis, coord );
  747.     if( offset < 0 ) {
  748.         offset = 0;
  749.     } else if( offset > axis->size ) {
  750.         offset = axis->size;
  751.     }
  752.     axis->lastdata = coord;
  753.     }
  754.  
  755.     return( offset );
  756. } /* updateCoord */
  757.  
  758.  
  759. /*  internal use only!!
  760.  *  do an area fill on the 4 pairs of coordinates,
  761.  *  clipped to the rastport's bitmap size
  762.  *  NOTE: the TmpRas should be (at least) the same size as this bitmap
  763. static VOID doBlock( rp, x1, y1, x2, y2, x3, y3, x4, y4 )
  764.     register struct RastPort    *rp;
  765.     SHORT            x1, y1, x2, y2, x3, y3, x4, y4;
  766. {
  767.     SHORT   width, height;
  768.  
  769.     width = rp->BitMap->BytesPerRow << 3;
  770.     height = rp->BitMap->Rows;
  771.  
  772.     if( x1 < 0 ) x1 = 0;
  773.     else if( x1 > width ) x1 = width;
  774.     if( x2 < 0 ) x2 = 0;
  775.     else if( x2 > width ) x2 = width;
  776.     if( x3 < 0 ) x3 = 0;
  777.     else if( x3 > width ) x3 = width;
  778.     if( x4 < 0 ) x4 = 0;
  779.     else if( x4 > width ) x4 = width;
  780.  
  781.     if( x1 == x3 ) return;
  782.  
  783.     if( y1 < 0 ) y1 = 0;
  784.     else if( y1 > height ) y1 = height;
  785.     if( y2 < 0 ) y2 = 0;
  786.     else if( y2 > height ) y2 = height;
  787.     if( y3 < 0 ) y3 = 0;
  788.     else if( y3 > height ) y3 = height;
  789.     if( y4 < 0 ) y4 = 0;
  790.     else if( y4 > height ) y4 = height;
  791.  
  792.     if( y1 == y3 ) return;
  793.  
  794.     AreaMove( rp, (long) x1, (long) y1 );
  795.     AreaDraw( rp, (long) x2, (long) y2 );
  796.     AreaDraw( rp, (long) x3, (long) y3 );
  797.     AreaDraw( rp, (long) x4, (long) y4 );
  798.     AreaEnd( rp );
  799. } /* doBlock */
  800.