Actual source code: axis.c

      1: #define PETSC_DLL
      2: /*
      3:    This file contains a simple routine for generating a 2-d axis.
      4: */

     6:  #include petsc.h

      8: PetscCookie DRAWAXIS_COOKIE = 0;

     10: struct _p_DrawAxis {
     11:   PETSCHEADER(int);
     12:     PetscReal      xlow,ylow,xhigh,yhigh;     /* User - coord limits */
     13:     PetscErrorCode (*ylabelstr)(PetscReal,PetscReal,char **);/* routines to generate labels */
     14:     PetscErrorCode (*xlabelstr)(PetscReal,PetscReal,char **);
     15:     PetscErrorCode (*xticks)(PetscReal,PetscReal,int,int*,PetscReal*,int);
     16:     PetscErrorCode (*yticks)(PetscReal,PetscReal,int,int*,PetscReal*,int);
     17:                                           /* location and size of ticks */
     18:     PetscDraw  win;
     19:     int        ac,tc,cc;                     /* axis,tick, character color */
     20:     char       *xlabel,*ylabel,*toplabel;
     21:     PetscTruth hold;
     22: };

     24: #define MAXSEGS 20

     26: EXTERN PetscErrorCode    PetscADefTicks(PetscReal,PetscReal,int,int*,PetscReal*,int);
     27: EXTERN PetscErrorCode    PetscADefLabel(PetscReal,PetscReal,char**);
     28: static PetscErrorCode    PetscAGetNice(PetscReal,PetscReal,int,PetscReal*);
     29: static PetscErrorCode    PetscAGetBase(PetscReal,PetscReal,int,PetscReal*,int*);

     33: static PetscErrorCode PetscRint(PetscReal x,PetscReal *result)
     34: {
     36:   if (x > 0) *result = floor(x + 0.5);
     37:   else       *result = floor(x - 0.5);
     38:   return(0);
     39: }

     43: /*@C
     44:    PetscDrawAxisCreate - Generate the axis data structure.

     46:    Collective over PetscDraw

     48:    Input Parameters:
     49: .  win - PetscDraw object where axis to to be made

     51:    Ouput Parameters:
     52: .  axis - the axis datastructure

     54:    Level: advanced

     56: @*/
     57: PetscErrorCode PETSC_DLLEXPORT PetscDrawAxisCreate(PetscDraw draw,PetscDrawAxis *axis)
     58: {
     59:   PetscDrawAxis  ad;
     60:   PetscObject    obj = (PetscObject)draw;
     62:   PetscTruth     isnull;

     67:   PetscTypeCompare(obj,PETSC_DRAW_NULL,&isnull);
     68:   if (isnull) {
     69:     PetscDrawOpenNull(obj->comm,(PetscDraw*)axis);
     70:     (*axis)->win = draw;
     71:     return(0);
     72:   }
     73:   PetscHeaderCreate(ad,_p_DrawAxis,int,DRAWAXIS_COOKIE,0,"PetscDrawAxis",obj->comm,PetscDrawAxisDestroy,0);
     74:   PetscLogObjectParent(draw,ad);
     75:   ad->xticks    = PetscADefTicks;
     76:   ad->yticks    = PetscADefTicks;
     77:   ad->xlabelstr = PetscADefLabel;
     78:   ad->ylabelstr = PetscADefLabel;
     79:   ad->win       = draw;
     80:   ad->ac        = PETSC_DRAW_BLACK;
     81:   ad->tc        = PETSC_DRAW_BLACK;
     82:   ad->cc        = PETSC_DRAW_BLACK;
     83:   ad->xlabel    = 0;
     84:   ad->ylabel    = 0;
     85:   ad->toplabel  = 0;

     87:   *axis = ad;
     88:   return(0);
     89: }

     93: /*@C
     94:     PetscDrawAxisDestroy - Frees the space used by an axis structure.

     96:     Collective over PetscDrawAxis

     98:     Input Parameters:
     99: .   axis - the axis context
    100:
    101:     Level: advanced

    103: @*/
    104: PetscErrorCode PETSC_DLLEXPORT PetscDrawAxisDestroy(PetscDrawAxis axis)
    105: {

    109:   if (!axis) return(0);
    110:   if (--axis->refct > 0) return(0);

    112:   PetscStrfree(axis->toplabel);
    113:   PetscStrfree(axis->xlabel);
    114:   PetscStrfree(axis->ylabel);
    115:   PetscHeaderDestroy(axis);
    116:   return(0);
    117: }

    121: /*@
    122:     PetscDrawAxisSetColors -  Sets the colors to be used for the axis,
    123:                          tickmarks, and text.

    125:     Not Collective (ignored on all processors except processor 0 of PetscDrawAxis)

    127:     Input Parameters:
    128: +   axis - the axis
    129: .   ac - the color of the axis lines
    130: .   tc - the color of the tick marks
    131: -   cc - the color of the text strings

    133:     Level: advanced

    135: @*/
    136: PetscErrorCode PETSC_DLLEXPORT PetscDrawAxisSetColors(PetscDrawAxis axis,int ac,int tc,int cc)
    137: {
    139:   if (!axis) return(0);
    140:   axis->ac = ac; axis->tc = tc; axis->cc = cc;
    141:   return(0);
    142: }

    146: /*@C
    147:     PetscDrawAxisSetLabels -  Sets the x and y axis labels.

    149:     Not Collective (ignored on all processors except processor 0 of PetscDrawAxis)

    151:     Input Parameters:
    152: +   axis - the axis
    153: .   top - the label at the top of the image
    154: -   xlabel,ylabel - the labes for the x and y axis

    156:     Level: advanced

    158: @*/
    159: PetscErrorCode PETSC_DLLEXPORT PetscDrawAxisSetLabels(PetscDrawAxis axis,const char top[],const char xlabel[],const char ylabel[])
    160: {

    164:   if (!axis) return(0);
    165:   PetscStrallocpy(xlabel,&axis->xlabel);
    166:   PetscStrallocpy(ylabel,&axis->ylabel);
    167:   PetscStrallocpy(top,&axis->toplabel);
    168:   return(0);
    169: }

    173: /*@
    174:     PetscDrawAxisSetHoldLimits -  Causes an axis to keep the same limits until this is called
    175:         again
    176:
    177:     Not Collective (ignored on all processors except processor 0 of PetscDrawAxis)

    179:     Input Parameters:
    180: +   axis - the axis
    181: -   hold - PETSC_TRUE - hold current limits, PETSC_FALSE allow limits to be changed

    183:     Level: advanced

    185:     Notes:
    186:         Once this has been called with PETSC_TRUE the limits will not change if you call
    187:      PetscDrawAxisSetLimits() until you call this with PETSC_FALSE
    188:
    189: .seealso:  PetscDrawAxisSetLimits()

    191: @*/
    192: PetscErrorCode PETSC_DLLEXPORT PetscDrawAxisSetHoldLimits(PetscDrawAxis axis,PetscTruth hold)
    193: {
    195:   if (!axis) return(0);
    196:   axis->hold = hold;
    197:   return(0);
    198: }

    202: /*@
    203:     PetscDrawAxisSetLimits -  Sets the limits (in user coords) of the axis
    204:
    205:     Not Collective (ignored on all processors except processor 0 of PetscDrawAxis)

    207:     Input Parameters:
    208: +   axis - the axis
    209: .   xmin,xmax - limits in x
    210: -   ymin,ymax - limits in y

    212:     Level: advanced

    214: .seealso:  PetscDrawAxisSetHoldLimits()

    216: @*/
    217: PetscErrorCode PETSC_DLLEXPORT PetscDrawAxisSetLimits(PetscDrawAxis axis,PetscReal xmin,PetscReal xmax,PetscReal ymin,PetscReal ymax)
    218: {
    220:   if (!axis) return(0);
    221:   if (axis->hold) return(0);
    222:   axis->xlow = xmin;
    223:   axis->xhigh= xmax;
    224:   axis->ylow = ymin;
    225:   axis->yhigh= ymax;
    226:   return(0);
    227: }

    231: /*@
    232:     PetscDrawAxisDraw - PetscDraws an axis.

    234:     Not Collective (ignored on all processors except processor 0 of PetscDrawAxis)

    236:     Input Parameter:
    237: .   axis - Axis structure

    239:     Level: advanced

    241:     Note:
    242:     This draws the actual axis.  The limits etc have already been set.
    243:     By picking special routines for the ticks and labels, special
    244:     effects may be generated.  These routines are part of the Axis
    245:     structure (axis).
    246: @*/
    247: PetscErrorCode PETSC_DLLEXPORT PetscDrawAxisDraw(PetscDrawAxis axis)
    248: {
    249:   int            i,ntick,numx,numy,ac = axis->ac,tc = axis->tc,cc = axis->cc,rank;
    250:   size_t         len;
    251:   PetscReal      tickloc[MAXSEGS],sep,h,w,tw,th,xl,xr,yl,yr;
    252:   char           *p;
    253:   PetscDraw      draw = axis->win;

    257:   if (!axis) return(0);
    258:   MPI_Comm_rank(axis->comm,&rank);
    259:   if (rank) return(0);

    261:   if (axis->xlow == axis->xhigh) {axis->xlow -= .5; axis->xhigh += .5;}
    262:   if (axis->ylow == axis->yhigh) {axis->ylow -= .5; axis->yhigh += .5;}
    263:   xl = axis->xlow; xr = axis->xhigh; yl = axis->ylow; yr = axis->yhigh;
    264:   PetscDrawSetCoordinates(draw,xl,yl,xr,yr);
    265:   PetscDrawStringGetSize(draw,&tw,&th);
    266:   numx = (int)(.15*(xr-xl)/tw); if (numx > 6) numx = 6; if (numx< 2) numx = 2;
    267:   numy = (int)(.5*(yr-yl)/th); if (numy > 6) numy = 6; if (numy< 2) numy = 2;
    268:   xl -= 8*tw; xr += 2*tw; yl -= 2.5*th; yr += 2*th;
    269:   if (axis->xlabel) yl -= 2*th;
    270:   if (axis->ylabel) xl -= 2*tw;
    271:   PetscDrawSetCoordinates(draw,xl,yl,xr,yr);
    272:   PetscDrawStringGetSize(draw,&tw,&th);

    274:   PetscDrawLine(draw,axis->xlow,axis->ylow,axis->xhigh,axis->ylow,ac);
    275:   PetscDrawLine(draw,axis->xlow,axis->ylow,axis->xlow,axis->yhigh,ac);

    277:   if (axis->toplabel) {
    278:      PetscStrlen(axis->toplabel,&len);
    279:     w    = xl + .5*(xr - xl) - .5*len*tw;
    280:     h    = axis->yhigh;
    281:     PetscDrawString(draw,w,h,cc,axis->toplabel);
    282:   }

    284:   /* PetscDraw the ticks and labels */
    285:   if (axis->xticks) {
    286:     (*axis->xticks)(axis->xlow,axis->xhigh,numx,&ntick,tickloc,MAXSEGS);
    287:     /* PetscDraw in tick marks */
    288:     for (i=0; i<ntick; i++) {
    289:       PetscDrawLine(draw,tickloc[i],axis->ylow-.5*th,tickloc[i],axis->ylow+.5*th,tc);
    290:     }
    291:     /* label ticks */
    292:     for (i=0; i<ntick; i++) {
    293:         if (axis->xlabelstr) {
    294:             if (i < ntick - 1) sep = tickloc[i+1] - tickloc[i];
    295:             else if (i > 0)    sep = tickloc[i]   - tickloc[i-1];
    296:             else               sep = 0.0;
    297:             (*axis->xlabelstr)(tickloc[i],sep,&p);
    298:             PetscStrlen(p,&len);
    299:             w    = .5*len*tw;
    300:             PetscDrawString(draw,tickloc[i]-w,axis->ylow-1.2*th,cc,p);
    301:         }
    302:     }
    303:   }
    304:   if (axis->xlabel) {
    305:     PetscStrlen(axis->xlabel,&len);
    306:     w    = xl + .5*(xr - xl) - .5*len*tw;
    307:     h    = axis->ylow - 2.5*th;
    308:     PetscDrawString(draw,w,h,cc,axis->xlabel);
    309:   }
    310:   if (axis->yticks) {
    311:     (*axis->yticks)(axis->ylow,axis->yhigh,numy,&ntick,tickloc,MAXSEGS);
    312:     /* PetscDraw in tick marks */
    313:     for (i=0; i<ntick; i++) {
    314:       PetscDrawLine(draw,axis->xlow -.5*tw,tickloc[i],axis->xlow+.5*tw,tickloc[i],tc);
    315:     }
    316:     /* label ticks */
    317:     for (i=0; i<ntick; i++) {
    318:         if (axis->ylabelstr) {
    319:             if (i < ntick - 1) sep = tickloc[i+1] - tickloc[i];
    320:             else if (i > 0)    sep = tickloc[i]   - tickloc[i-1];
    321:             else               sep = 0.0;
    322:             (*axis->xlabelstr)(tickloc[i],sep,&p);
    323:             PetscStrlen(p,&len);
    324:             w    = axis->xlow - len * tw - 1.2*tw;
    325:             PetscDrawString(draw,w,tickloc[i]-.5*th,cc,p);
    326:         }
    327:     }
    328:   }
    329:   if (axis->ylabel) {
    330:     PetscStrlen(axis->ylabel,&len);
    331:     h    = yl + .5*(yr - yl) + .5*len*th;
    332:     w    = xl + .5*tw;
    333:     PetscDrawStringVertical(draw,w,h,cc,axis->ylabel);
    334:   }
    335:   return(0);
    336: }

    340: /*
    341:     Removes all zeros but one from .0000
    342: */
    343: static PetscErrorCode PetscStripAllZeros(char *buf)
    344: {
    346:   size_t         i,n;

    349:   PetscStrlen(buf,&n);
    350:   if (buf[0] != '.') return(0);
    351:   for (i=1; i<n; i++) {
    352:     if (buf[i] != '0') return(0);
    353:   }
    354:   buf[0] = '0';
    355:   buf[1] = 0;
    356:   return(0);
    357: }

    361: /*
    362:     Removes trailing zeros
    363: */
    364: static PetscErrorCode PetscStripTrailingZeros(char *buf)
    365: {
    367:   char           *found;
    368:   size_t         i,n,m = PETSC_MAX_INT;

    371:   /* if there is an e in string DO NOT strip trailing zeros */
    372:   PetscStrchr(buf,'e',&found);
    373:   if (found) return(0);

    375:   PetscStrlen(buf,&n);
    376:   /* locate decimal point */
    377:   for (i=0; i<n; i++) {
    378:     if (buf[i] == '.') {m = i; break;}
    379:   }
    380:   /* if not decimal point then no zeros to remove */
    381:   if (m == PETSC_MAX_INT) return(0);
    382:   /* start at right end of string removing 0s */
    383:   for (i=n-1; i>m; i++) {
    384:     if (buf[i] != '0') return(0);
    385:     buf[i] = 0;
    386:   }
    387:   return(0);
    388: }

    392: /*
    393:     Removes leading 0 from 0.22 or -0.22
    394: */
    395: static PetscErrorCode PetscStripInitialZero(char *buf)
    396: {
    398:   size_t         i,n;

    401:   PetscStrlen(buf,&n);
    402:   if (buf[0] == '0') {
    403:     for (i=0; i<n; i++) {
    404:       buf[i] = buf[i+1];
    405:     }
    406:   } else if (buf[0] == '-' && buf[1] == '0') {
    407:     for (i=1; i<n; i++) {
    408:       buf[i] = buf[i+1];
    409:     }
    410:   }
    411:   return(0);
    412: }

    416: /*
    417:      Removes the extraneous zeros in numbers like 1.10000e6
    418: */
    419: static PetscErrorCode PetscStripZeros(char *buf)
    420: {
    422:   size_t         i,j,n;

    425:   PetscStrlen(buf,&n);
    426:   if (n<5) return(0);
    427:   for (i=1; i<n-1; i++) {
    428:     if (buf[i] == 'e' && buf[i-1] == '0') {
    429:       for (j=i; j<n+1; j++) buf[j-1] = buf[j];
    430:       PetscStripZeros(buf);
    431:       return(0);
    432:     }
    433:   }
    434:   return(0);
    435: }

    439: /*
    440:       Removes the plus in something like 1.1e+2
    441: */
    442: static PetscErrorCode PetscStripZerosPlus(char *buf)
    443: {
    445:   size_t         i,j,n;

    448:   PetscStrlen(buf,&n);
    449:   if (n<5) return(0);
    450:   for (i=1; i<n-2; i++) {
    451:     if (buf[i] == '+') {
    452:       if (buf[i+1] == '0') {
    453:         for (j=i+1; j<n+1; j++) buf[j-1] = buf[j+1];
    454:         return(0);
    455:       } else {
    456:         for (j=i+1; j<n+1; j++) buf[j] = buf[j+1];
    457:         return(0);
    458:       }
    459:     } else if (buf[i] == '-') {
    460:       if (buf[i+1] == '0') {
    461:         for (j=i+1; j<n+1; j++) buf[j] = buf[j+1];
    462:         return(0);
    463:       }
    464:     }
    465:   }
    466:   return(0);
    467: }

    471: /*
    472:    val is the label value.  sep is the separation to the next (or previous)
    473:    label; this is useful in determining how many significant figures to
    474:    keep.
    475:  */
    476: PetscErrorCode PetscADefLabel(PetscReal val,PetscReal sep,char **p)
    477: {
    478:   static char    buf[40];
    479:   char           fmat[10];
    481:   int            w,d;
    482:   PetscReal      rval;

    485:   /* Find the string */
    486:   if (PetscAbsReal(val)/sep <  1.e-6) {
    487:     buf[0] = '0'; buf[1] = 0;
    488:   } else if (PetscAbsReal(val) < 1.0e6 && PetscAbsReal(val) > 1.e-4) {
    489:     /* Compute the number of digits */
    490:     w = 0;
    491:     d = 0;
    492:     if (sep > 0.0) {
    493:         d = (int)ceil(- log10 (sep));
    494:         if (d < 0) d = 0;
    495:         if (PetscAbsReal(val) < 1.0e-6*sep) {
    496:             /* This is the case where we are near zero and less than a small
    497:                fraction of the sep.  In this case, we use 0 as the value */
    498:             val = 0.0;
    499:             w   = d;
    500:         }
    501:         else if (!val) w   = d;
    502:         else w = (int)(ceil(log10(PetscAbsReal(val))) + d);
    503:         if (w < 1)   w ++;
    504:         if (val < 0) w ++;
    505:     }

    507:     PetscRint(val,&rval);
    508:     if (rval == val) {
    509:         if (w > 0) sprintf(fmat,"%%%dd",w);
    510:         else {PetscStrcpy(fmat,"%d");}
    511:         sprintf(buf,fmat,(int)val);
    512:         PetscStripInitialZero(buf);
    513:         PetscStripAllZeros(buf);
    514:         PetscStripTrailingZeros(buf);
    515:     } else {
    516:         /* The code used here is inappropriate for a val of 0, which
    517:            tends to print with an excessive numer of digits.  In this
    518:            case, we should look at the next/previous values and
    519:            use those widths */
    520:         if (w > 0) sprintf(fmat,"%%%d.%dlf",w + 1,d);
    521:         else {PetscStrcpy(fmat,"%lf");}
    522:         sprintf(buf,fmat,val);
    523:         PetscStripInitialZero(buf);
    524:         PetscStripAllZeros(buf);
    525:         PetscStripTrailingZeros(buf);
    526:     }
    527:   } else {
    528:     sprintf(buf,"%e",val);
    529:     /* remove the extraneous 0 before the e */
    530:     PetscStripZeros(buf);
    531:     PetscStripZerosPlus(buf);
    532:     PetscStripInitialZero(buf);
    533:     PetscStripAllZeros(buf);
    534:     PetscStripTrailingZeros(buf);
    535:   }
    536:   *p =buf;
    537:   return(0);
    538: }

    542: /* Finds "nice" locations for the ticks */
    543: PetscErrorCode PetscADefTicks(PetscReal low,PetscReal high,int num,int *ntick,PetscReal * tickloc,int  maxtick)
    544: {
    546:   int            i,power;
    547:   PetscReal      x,base;

    550:   /* patch if low == high */
    551:   if (low == high) {
    552:     low  -= .01;
    553:     high += .01;
    554:   }

    556:   /*  if (PetscAbsReal(low-high) < 1.e-8) {
    557:     low  -= .01;
    558:     high += .01;
    559:   } */

    561:   PetscAGetBase(low,high,num,&base,&power);
    562:   PetscAGetNice(low,base,-1,&x);

    564:   /* Values are of the form j * base */
    565:   /* Find the starting value */
    566:   if (x < low) x += base;

    568:   i = 0;
    569:   while (i < maxtick && x <= high) {
    570:     tickloc[i++] = x;
    571:     x += base;
    572:   }
    573:   *ntick = i;

    575:   if (i < 2 && num < 10) {
    576:     PetscADefTicks(low,high,num+1,ntick,tickloc,maxtick);
    577:   }
    578:   return(0);
    579: }

    581: #define EPS 1.e-6

    585: static PetscErrorCode PetscExp10(PetscReal d,PetscReal *result)
    586: {
    588:   *result = pow(10.0,d);
    589:   return(0);
    590: }

    594: static PetscErrorCode PetscMod(PetscReal x,PetscReal y,PetscReal *result)
    595: {
    596:   int     i;

    599:   i   = ((int)x) / ((int)y);
    600:   x   = x - i * y;
    601:   while (x > y) x -= y;
    602:   *result = x;
    603:   return(0);
    604: }

    608: static PetscErrorCode PetscCopysign(PetscReal a,PetscReal b,PetscReal *result)
    609: {
    611:   if (b >= 0) *result = a;
    612:   else        *result = -a;
    613:   return(0);
    614: }

    618: /*
    619:     Given a value "in" and a "base", return a nice value.
    620:     based on "sign", extend up (+1) or down (-1)
    621:  */
    622: static PetscErrorCode PetscAGetNice(PetscReal in,PetscReal base,int sign,PetscReal *result)
    623: {
    624:   PetscReal      etmp,s,s2,m;

    628:   PetscCopysign (0.5,(double)sign,&s);
    629:   etmp    = in / base + 0.5 + s;
    630:   PetscCopysign (0.5,etmp,&s);
    631:   PetscCopysign (EPS * etmp,(double)sign,&s2);
    632:   etmp    = etmp - 0.5 + s - s2;
    633:   PetscMod(etmp,1.0,&m);
    634:   etmp    = base * (etmp -  m);
    635:   *result = etmp;
    636:   return(0);
    637: }

    641: static PetscErrorCode PetscAGetBase(PetscReal vmin,PetscReal vmax,int num,PetscReal*Base,int*power)
    642: {
    643:   PetscReal        base,ftemp,e10;
    644:   static PetscReal base_try[5] = {10.0,5.0,2.0,1.0,0.5};
    645:   PetscErrorCode   ierr;
    646:   int              i;

    649:   /* labels of the form n * BASE */
    650:   /* get an approximate value for BASE */
    651:   base    = (vmax - vmin) / (double)(num + 1);

    653:   /* make it of form   m x 10^power,  m in [1.0, 10) */
    654:   if (base <= 0.0) {
    655:     base    = PetscAbsReal(vmin);
    656:     if (base < 1.0) base = 1.0;
    657:   }
    658:   ftemp   = log10((1.0 + EPS) * base);
    659:   if (ftemp < 0.0)  ftemp   -= 1.0;
    660:   *power  = (int)ftemp;
    661:   PetscExp10((double)- *power,&e10);
    662:   base    = base * e10;
    663:   if (base < 1.0) base    = 1.0;
    664:   /* now reduce it to one of 1, 2, or 5 */
    665:   for (i=1; i<5; i++) {
    666:     if (base >= base_try[i]) {
    667:       PetscExp10((double)*power,&e10);
    668:       base = base_try[i-1] * e10;
    669:       if (i == 1) *power    = *power + 1;
    670:       break;
    671:     }
    672:   }
    673:   *Base   = base;
    674:   return(0);
    675: }