Actual source code: axis.c

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

 5:  #include petsc.h

  7: PetscCookie DRAWAXIS_COOKIE = 0;

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

 23: #define MAXSEGS 20

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

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

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

 45:    Collective over PetscDraw

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

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

 53:    Level: advanced

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

 66:   PetscTypeCompare(obj,PETSC_DRAW_NULL,&isnull);
 67:   if (isnull) {
 68:     PetscDrawOpenNull(obj->comm,(PetscDraw*)axis);
 69:     (*axis)->win = draw;
 70:     return(0);
 71:   }
 72:   PetscHeaderCreate(ad,_p_DrawAxis,int,DRAWAXIS_COOKIE,0,"PetscDrawAxis",obj->comm,PetscDrawAxisDestroy,0);
 73:   PetscLogObjectCreate(ad);
 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 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:   PetscLogObjectDestroy(axis);
116:   PetscHeaderDestroy(axis);
117:   return(0);
118: }

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

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

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

134:     Level: advanced

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

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

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

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

157:     Level: advanced

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

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

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

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

184:     Level: advanced

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

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

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

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

213:     Level: advanced

215: .seealso:  PetscDrawAxisSetHoldLimits()

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

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

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

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

240:     Level: advanced

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

582: #define EPS 1.e-6

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

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

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

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

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

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

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

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

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