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: /*@
 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  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: /*@
 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  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  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  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  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  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  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 = 0.0,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: }