Actual source code: axis.c

  1: /*$Id: axis.c,v 1.76 2001/08/07 21:28:45 bsmith Exp $*/
  2: /*
  3:    This file contains a simple routine for generating a 2-d axis.
  4: */

 6:  #include petsc.h

  8: int DRAWAXIS_COOKIE;

 10: struct _p_DrawAxis {
 11:     PETSCHEADER(int)
 12:     PetscReal  xlow,ylow,xhigh,yhigh;     /* User - coord limits */
 13:     int        (*ylabelstr)(PetscReal,PetscReal,char **),/* routines to generate labels */
 14:                (*xlabelstr)(PetscReal,PetscReal,char **);
 15:     int        (*xticks)(PetscReal,PetscReal,int,int*,PetscReal*,int),
 16:                (*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 int    PetscADefTicks(PetscReal,PetscReal,int,int*,PetscReal*,int);
 27: EXTERN int    PetscADefLabel(PetscReal,PetscReal,char**);
 28: static int    PetscAGetNice(PetscReal,PetscReal,int,PetscReal*);
 29: static int    PetscAGetBase(PetscReal,PetscReal,int,PetscReal*,int*);

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

 39: /*@C
 40:    PetscDrawAxisCreate - Generate the axis data structure.

 42:    Collective over PetscDraw

 44:    Input Parameters:
 45: .  win - PetscDraw object where axis to to be made

 47:    Ouput Parameters:
 48: .  axis - the axis datastructure

 50:    Level: advanced

 52: @*/
 53: int PetscDrawAxisCreate(PetscDraw draw,PetscDrawAxis *axis)
 54: {
 55:   PetscDrawAxis ad;
 56:   PetscObject   obj = (PetscObject)draw;
 57:   int           ierr;
 58:   PetscTruth    isnull;

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

 84:   *axis = ad;
 85:   return(0);
 86: }

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

 91:     Collective over PetscDrawAxis

 93:     Input Parameters:
 94: .   axis - the axis context
 95:  
 96:     Level: advanced

 98: @*/
 99: int PetscDrawAxisDestroy(PetscDrawAxis axis)
100: {

104:   if (!axis) return(0);
105:   if (--axis->refct > 0) return(0);

107:   PetscStrfree(axis->toplabel);
108:   PetscStrfree(axis->xlabel);
109:   PetscStrfree(axis->ylabel);
110:   PetscLogObjectDestroy(axis);
111:   PetscHeaderDestroy(axis);
112:   return(0);
113: }

115: /*@
116:     PetscDrawAxisSetColors -  Sets the colors to be used for the axis,       
117:                          tickmarks, and text.

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

121:     Input Parameters:
122: +   axis - the axis
123: .   ac - the color of the axis lines
124: .   tc - the color of the tick marks
125: -   cc - the color of the text strings

127:     Level: advanced

129: @*/
130: int PetscDrawAxisSetColors(PetscDrawAxis axis,int ac,int tc,int cc)
131: {
133:   if (!axis) return(0);
134:   axis->ac = ac; axis->tc = tc; axis->cc = cc;
135:   return(0);
136: }

138: /*@C
139:     PetscDrawAxisSetLabels -  Sets the x and y axis labels.

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

143:     Input Parameters:
144: +   axis - the axis
145: .   top - the label at the top of the image
146: -   xlabel,ylabel - the labes for the x and y axis

148:     Level: advanced

150: @*/
151: int PetscDrawAxisSetLabels(PetscDrawAxis axis,char* top,char *xlabel,char *ylabel)
152: {

156:   if (!axis) return(0);
157:   PetscStrallocpy(xlabel,&axis->xlabel);
158:   PetscStrallocpy(ylabel,&axis->ylabel);
159:   PetscStrallocpy(top,&axis->toplabel);
160:   return(0);
161: }

163: /*@
164:     PetscDrawAxisSetHoldLimits -  Causes an axis to keep the same limits until this is called
165:         again
166:     
167:     Not Collective (ignored on all processors except processor 0 of PetscDrawAxis)

169:     Input Parameters:
170: +   axis - the axis
171: -   hold - PETSC_TRUE - hold current limits, PETSC_FALSE allow limits to be changed

173:     Level: advanced

175:     Notes:
176:         Once this has been called with PETSC_TRUE the limits will not change if you call
177:      PetscDrawAxisSetLimits() until you call this with PETSC_FALSE
178:  
179: .seealso:  PetscDrawAxisSetLimits()

181: @*/
182: int PetscDrawAxisSetHoldLimits(PetscDrawAxis axis,PetscTruth hold)
183: {
185:   if (!axis) return(0);
186:   axis->hold = hold;
187:   return(0);
188: }

190: /*@
191:     PetscDrawAxisSetLimits -  Sets the limits (in user coords) of the axis
192:     
193:     Not Collective (ignored on all processors except processor 0 of PetscDrawAxis)

195:     Input Parameters:
196: +   axis - the axis
197: .   xmin,xmax - limits in x
198: -   ymin,ymax - limits in y

200:     Level: advanced

202: .seealso:  PetscDrawAxisSetHoldLimits()

204: @*/
205: int PetscDrawAxisSetLimits(PetscDrawAxis axis,PetscReal xmin,PetscReal xmax,PetscReal ymin,PetscReal ymax)
206: {
208:   if (!axis) return(0);
209:   if (axis->hold) return(0);
210:   axis->xlow = xmin;
211:   axis->xhigh= xmax;
212:   axis->ylow = ymin;
213:   axis->yhigh= ymax;
214:   return(0);
215: }

217: /*@
218:     PetscDrawAxisDraw - PetscDraws an axis.

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

222:     Input Parameter:
223: .   axis - Axis structure

225:     Level: advanced

227:     Note:
228:     This draws the actual axis.  The limits etc have already been set.
229:     By picking special routines for the ticks and labels, special
230:     effects may be generated.  These routines are part of the Axis
231:     structure (axis).
232: @*/
233: int PetscDrawAxisDraw(PetscDrawAxis axis)
234: {
235:   int       i,ierr,ntick,numx,numy,ac = axis->ac,tc = axis->tc,cc = axis->cc,rank,len;
236:   PetscReal tickloc[MAXSEGS],sep,h,w,tw,th,xl,xr,yl,yr;
237:   char      *p;
238:   PetscDraw draw = axis->win;
239: 
241:   if (!axis) return(0);
242:   MPI_Comm_rank(axis->comm,&rank);
243:   if (rank) return(0);

245:   if (axis->xlow == axis->xhigh) {axis->xlow -= .5; axis->xhigh += .5;}
246:   if (axis->ylow == axis->yhigh) {axis->ylow -= .5; axis->yhigh += .5;}
247:   xl = axis->xlow; xr = axis->xhigh; yl = axis->ylow; yr = axis->yhigh;
248:   PetscDrawSetCoordinates(draw,xl,yl,xr,yr);
249:   PetscDrawStringGetSize(draw,&tw,&th);
250:   numx = (int)(.15*(xr-xl)/tw); if (numx > 6) numx = 6; if (numx< 2) numx = 2;
251:   numy = (int)(.5*(yr-yl)/th); if (numy > 6) numy = 6; if (numy< 2) numy = 2;
252:   xl -= 8*tw; xr += 2*tw; yl -= 2.5*th; yr += 2*th;
253:   if (axis->xlabel) yl -= 2*th;
254:   if (axis->ylabel) xl -= 2*tw;
255:   PetscDrawSetCoordinates(draw,xl,yl,xr,yr);
256:   PetscDrawStringGetSize(draw,&tw,&th);

258:   PetscDrawLine(draw,axis->xlow,axis->ylow,axis->xhigh,axis->ylow,ac);
259:   PetscDrawLine(draw,axis->xlow,axis->ylow,axis->xlow,axis->yhigh,ac);

261:   if (axis->toplabel) {
262:      PetscStrlen(axis->toplabel,&len);
263:     w    = xl + .5*(xr - xl) - .5*len*tw;
264:     h    = axis->yhigh;
265:     PetscDrawString(draw,w,h,cc,axis->toplabel);
266:   }

268:   /* PetscDraw the ticks and labels */
269:   if (axis->xticks) {
270:     (*axis->xticks)(axis->xlow,axis->xhigh,numx,&ntick,tickloc,MAXSEGS);
271:     /* PetscDraw in tick marks */
272:     for (i=0; i<ntick; i++) {
273:       PetscDrawLine(draw,tickloc[i],axis->ylow-.5*th,tickloc[i],axis->ylow+.5*th,tc);
274:     }
275:     /* label ticks */
276:     for (i=0; i<ntick; i++) {
277:         if (axis->xlabelstr) {
278:             if (i < ntick - 1) sep = tickloc[i+1] - tickloc[i];
279:             else if (i > 0)    sep = tickloc[i]   - tickloc[i-1];
280:             else               sep = 0.0;
281:             (*axis->xlabelstr)(tickloc[i],sep,&p);
282:             PetscStrlen(p,&len);
283:             w    = .5*len*tw;
284:             PetscDrawString(draw,tickloc[i]-w,axis->ylow-1.2*th,cc,p);
285:         }
286:     }
287:   }
288:   if (axis->xlabel) {
289:     PetscStrlen(axis->xlabel,&len);
290:     w    = xl + .5*(xr - xl) - .5*len*tw;
291:     h    = axis->ylow - 2.5*th;
292:     PetscDrawString(draw,w,h,cc,axis->xlabel);
293:   }
294:   if (axis->yticks) {
295:     (*axis->yticks)(axis->ylow,axis->yhigh,numy,&ntick,tickloc,MAXSEGS);
296:     /* PetscDraw in tick marks */
297:     for (i=0; i<ntick; i++) {
298:       PetscDrawLine(draw,axis->xlow -.5*tw,tickloc[i],axis->xlow+.5*tw,tickloc[i],tc);
299:     }
300:     /* label ticks */
301:     for (i=0; i<ntick; i++) {
302:         if (axis->ylabelstr) {
303:             if (i < ntick - 1) sep = tickloc[i+1] - tickloc[i];
304:             else if (i > 0)    sep = tickloc[i]   - tickloc[i-1];
305:             else               sep = 0.0;
306:             (*axis->xlabelstr)(tickloc[i],sep,&p);
307:             PetscStrlen(p,&len);
308:             w    = axis->xlow - len * tw - 1.2*tw;
309:             PetscDrawString(draw,w,tickloc[i]-.5*th,cc,p);
310:         }
311:     }
312:   }
313:   if (axis->ylabel) {
314:     PetscStrlen(axis->ylabel,&len);
315:     h    = yl + .5*(yr - yl) + .5*len*th;
316:     w    = xl + .5*tw;
317:     PetscDrawStringVertical(draw,w,h,cc,axis->ylabel);
318:   }
319:   return(0);
320: }

322: /*
323:     Removes all zeros but one from .0000 
324: */
325: static int PetscStripAllZeros(char *buf)
326: {
327:   int i,n,ierr;

330:   PetscStrlen(buf,&n);
331:   if (buf[0] != '.') return(0);
332:   for (i=1; i<n; i++) {
333:     if (buf[i] != '0') return(0);
334:   }
335:   buf[0] = '0';
336:   buf[1] = 0;
337:   return(0);
338: }

340: /*
341:     Removes trailing zeros
342: */
343: static int PetscStripTrailingZeros(char *buf)
344: {
345:   int  ierr,i,n,m = -1;
346:   char *found;

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

353:   PetscStrlen(buf,&n);
354:   /* locate decimal point */
355:   for (i=0; i<n; i++) {
356:     if (buf[i] == '.') {m = i; break;}
357:   }
358:   /* if not decimal point then no zeros to remove */
359:   if (m == -1) return(0);
360:   /* start at right end of string removing 0s */
361:   for (i=n-1; i>m; i++) {
362:     if (buf[i] != '0') return(0);
363:     buf[i] = 0;
364:   }
365:   return(0);
366: }

368: /*
369:     Removes leading 0 from 0.22 or -0.22
370: */
371: static int PetscStripInitialZero(char *buf)
372: {
373:   int i,n,ierr;

376:   PetscStrlen(buf,&n);
377:   if (buf[0] == '0') {
378:     for (i=0; i<n; i++) {
379:       buf[i] = buf[i+1];
380:     }
381:   } else if (buf[0] == '-' && buf[1] == '0') {
382:     for (i=1; i<n; i++) {
383:       buf[i] = buf[i+1];
384:     }
385:   }
386:   return(0);
387: }

389: /*
390:      Removes the extraneous zeros in numbers like 1.10000e6
391: */
392: static int PetscStripZeros(char *buf)
393: {
394:   int i,j,n,ierr;

397:   PetscStrlen(buf,&n);
398:   if (n<5) return(0);
399:   for (i=1; i<n-1; i++) {
400:     if (buf[i] == 'e' && buf[i-1] == '0') {
401:       for (j=i; j<n+1; j++) buf[j-1] = buf[j];
402:       PetscStripZeros(buf);
403:       return(0);
404:     }
405:   }
406:   return(0);
407: }

409: /*
410:       Removes the plus in something like 1.1e+2
411: */
412: static int PetscStripZerosPlus(char *buf)
413: {
414:   int i,j,n,ierr;

417:   PetscStrlen(buf,&n);
418:   if (n<5) return(0);
419:   for (i=1; i<n-2; i++) {
420:     if (buf[i] == '+') {
421:       if (buf[i+1] == '0') {
422:         for (j=i+1; j<n+1; j++) buf[j-1] = buf[j+1];
423:         return(0);
424:       } else {
425:         for (j=i+1; j<n+1; j++) buf[j] = buf[j+1];
426:         return(0);
427:       }
428:     } else if (buf[i] == '-') {
429:       if (buf[i+1] == '0') {
430:         for (j=i+1; j<n+1; j++) buf[j] = buf[j+1];
431:         return(0);
432:       }
433:     }
434:   }
435:   return(0);
436: }

438: /*
439:    val is the label value.  sep is the separation to the next (or previous)
440:    label; this is useful in determining how many significant figures to   
441:    keep.
442:  */
443: int PetscADefLabel(PetscReal val,PetscReal sep,char **p)
444: {
445:   static char buf[40];
446:   char        fmat[10];
447:   int         ierr,w,d;
448:   PetscReal   rval;

451:   /* Find the string */
452:   if (PetscAbsReal(val)/sep <  1.e-6) {
453:     buf[0] = '0'; buf[1] = 0;
454:   } else if (PetscAbsReal(val) < 1.0e6 && PetscAbsReal(val) > 1.e-4) {
455:     /* Compute the number of digits */
456:     w = 0;
457:     d = 0;
458:     if (sep > 0.0) {
459:         d = (int)ceil(- log10 (sep));
460:         if (d < 0) d = 0;
461:         if (PetscAbsReal(val) < 1.0e-6*sep) {
462:             /* This is the case where we are near zero and less than a small
463:                fraction of the sep.  In this case, we use 0 as the value */
464:             val = 0.0;
465:             w   = d;
466:         }
467:         else if (val == 0.0) w   = d;
468:         else w = (int)(ceil(log10(PetscAbsReal(val))) + d);
469:         if (w < 1)   w ++;
470:         if (val < 0) w ++;
471:     }

473:     PetscRint(val,&rval);
474:     if (rval == val) {
475:         if (w > 0) sprintf(fmat,"%%%dd",w);
476:         else {PetscStrcpy(fmat,"%d");}
477:         sprintf(buf,fmat,(int)val);
478:         PetscStripInitialZero(buf);
479:         PetscStripAllZeros(buf);
480:         PetscStripTrailingZeros(buf);
481:     } else {
482:         /* The code used here is inappropriate for a val of 0, which
483:            tends to print with an excessive numer of digits.  In this
484:            case, we should look at the next/previous values and 
485:            use those widths */
486:         if (w > 0) sprintf(fmat,"%%%d.%dlf",w + 1,d);
487:         else {PetscStrcpy(fmat,"%lf");}
488:         sprintf(buf,fmat,val);
489:         PetscStripInitialZero(buf);
490:         PetscStripAllZeros(buf);
491:         PetscStripTrailingZeros(buf);
492:     }
493:   } else {
494:     sprintf(buf,"%e",val);
495:     /* remove the extraneous 0 before the e */
496:     PetscStripZeros(buf);
497:     PetscStripZerosPlus(buf);
498:     PetscStripInitialZero(buf);
499:     PetscStripAllZeros(buf);
500:     PetscStripTrailingZeros(buf);
501:   }
502:   *p =buf;
503:   return(0);
504: }

506: /* Finds "nice" locations for the ticks */
507: int PetscADefTicks(PetscReal low,PetscReal high,int num,int *ntick,PetscReal * tickloc,int  maxtick)
508: {
509:   int       i,power,ierr;
510:   PetscReal x,base;

513:   /* patch if low == high */
514:   if (low == high) {
515:     low  -= .01;
516:     high += .01;
517:   }

519:   /*  if (PetscAbsReal(low-high) < 1.e-8) {
520:     low  -= .01;
521:     high += .01;
522:   } */

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

527:   /* Values are of the form j * base */
528:   /* Find the starting value */
529:   if (x < low) x += base;

531:   i = 0;
532:   while (i < maxtick && x <= high) {
533:     tickloc[i++] = x;
534:     x += base;
535:   }
536:   *ntick = i;

538:   if (i < 2 && num < 10) {
539:     PetscADefTicks(low,high,num+1,ntick,tickloc,maxtick);
540:   }
541:   return(0);
542: }

544: #define EPS 1.e-6

546: static int PetscExp10(PetscReal d,PetscReal *result)
547: {
549:   *result = pow(10.0,d);
550:   return(0);
551: }

553: static int PetscMod(PetscReal x,PetscReal y,PetscReal *result)
554: {
555:   int     i;

558:   i   = ((int)x) / ((int)y);
559:   x   = x - i * y;
560:   while (x > y) x -= y;
561:   *result = x;
562:   return(0);
563: }

565: static int PetscCopysign(PetscReal a,PetscReal b,PetscReal *result)
566: {
568:   if (b >= 0) *result = a;
569:   else        *result = -a;
570:   return(0);
571: }

573: /*
574:     Given a value "in" and a "base", return a nice value.
575:     based on "sign", extend up (+1) or down (-1)
576:  */
577: static int PetscAGetNice(PetscReal in,PetscReal base,int sign,PetscReal *result)
578: {
579:   PetscReal  etmp,s,s2,m;
580:   int     ierr;

583:   ierr    = PetscCopysign (0.5,(double)sign,&s);
584:   etmp    = in / base + 0.5 + s;
585:   ierr    = PetscCopysign (0.5,etmp,&s);
586:   ierr    = PetscCopysign (EPS * etmp,(double)sign,&s2);
587:   etmp    = etmp - 0.5 + s - s2;
588:   ierr    = PetscMod(etmp,1.0,&m);
589:   etmp    = base * (etmp -  m);
590:   *result = etmp;
591:   return(0);
592: }

594: static int PetscAGetBase(PetscReal vmin,PetscReal vmax,int num,PetscReal*Base,int*power)
595: {
596:   PetscReal        base,ftemp,e10;
597:   static PetscReal base_try[5] = {10.0,5.0,2.0,1.0,0.5};
598:   int              i,ierr;

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

605:   /* make it of form   m x 10^power,  m in [1.0, 10) */
606:   if (base <= 0.0) {
607:     base    = PetscAbsReal(vmin);
608:     if (base < 1.0) base = 1.0;
609:   }
610:   ftemp   = log10((1.0 + EPS) * base);
611:   if (ftemp < 0.0)  ftemp   -= 1.0;
612:   *power  = (int)ftemp;
613:   PetscExp10((double)- *power,&e10);
614:   base    = base * e10;
615:   if (base < 1.0) base    = 1.0;
616:   /* now reduce it to one of 1, 2, or 5 */
617:   for (i=1; i<5; i++) {
618:     if (base >= base_try[i]) {
619:       PetscExp10((double)*power,&e10);
620:       base = base_try[i-1] * e10;
621:       if (i == 1) *power    = *power + 1;
622:       break;
623:     }
624:   }
625:   *Base   = base;
626:   return(0);
627: }