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: }