xref: /petsc/src/sys/classes/draw/utils/axisc.c (revision f4d061e980d13bc62f06124c58b76593bdf99e72)
1 #include <petsc/private/drawimpl.h> /*I   "petscdraw.h"  I*/
2 
3 #define PETSC_DRAW_AXIS_MAX_SEGMENTS 20
4 PetscClassId PETSC_DRAWAXIS_CLASSID = 0;
5 
6 /*@
7    PetscDrawAxisCreate - Generate the axis data structure.
8 
9    Collective on draw
10 
11    Input Parameters:
12 .  win - `PetscDraw` object where axis to to be made
13 
14    Output Parameter:
15 .  axis - the axis datastructure
16 
17    Note:
18    The MPI communicator that owns the underlying draw object owns the `PetscDrawAxis` object, but calls to set `PetscDrawAxis` options are
19    ignored by all processes except the first MPI rank in the communicator
20 
21    Level: advanced
22 
23 .seealso: `PetscDrawLGCreate()`, `PetscDrawLG`, `PetscDrawSPCreate()`, `PetscDrawSP`, `PetscDrawHGCreate()`, `PetscDrawHG`, `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawLGGetAxis()`, `PetscDrawSPGetAxis()`,
24           `PetscDrawHGGetAxis()`, `PetscDrawBarGetAxis()`, `PetscDrawAxis`, `PetscDrawAxisDestroy()`, `PetscDrawAxisSetColors()`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisSetLimits()`, `PetscDrawAxisGetLimits()`, `PetscDrawAxisSetHoldLimits()`,
25           `PetscDrawAxisDraw()`
26 @*/
27 PetscErrorCode PetscDrawAxisCreate(PetscDraw draw, PetscDrawAxis *axis) {
28   PetscDrawAxis ad;
29 
30   PetscFunctionBegin;
31   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
32   PetscValidPointer(axis, 2);
33 
34   PetscCall(PetscHeaderCreate(ad, PETSC_DRAWAXIS_CLASSID, "DrawAxis", "Draw Axis", "Draw", PetscObjectComm((PetscObject)draw), PetscDrawAxisDestroy, NULL));
35   PetscCall(PetscLogObjectParent((PetscObject)draw, (PetscObject)ad));
36 
37   PetscCall(PetscObjectReference((PetscObject)draw));
38   ad->win = draw;
39 
40   ad->xticks    = PetscADefTicks;
41   ad->yticks    = PetscADefTicks;
42   ad->xlabelstr = PetscADefLabel;
43   ad->ylabelstr = PetscADefLabel;
44   ad->ac        = PETSC_DRAW_BLACK;
45   ad->tc        = PETSC_DRAW_BLACK;
46   ad->cc        = PETSC_DRAW_BLACK;
47   ad->xlabel    = NULL;
48   ad->ylabel    = NULL;
49   ad->toplabel  = NULL;
50 
51   *axis = ad;
52   PetscFunctionReturn(0);
53 }
54 
55 /*@
56     PetscDrawAxisDestroy - Frees the space used by an axis structure.
57 
58     Collective on axis
59 
60     Input Parameters:
61 .   axis - the axis context
62 
63     Level: advanced
64 
65 .seealso: `PetscDraw`, `PetscDrawAxisCreate()`, `PetscDrawAxis`
66 @*/
67 PetscErrorCode PetscDrawAxisDestroy(PetscDrawAxis *axis) {
68   PetscFunctionBegin;
69   if (!*axis) PetscFunctionReturn(0);
70   PetscValidHeaderSpecific(*axis, PETSC_DRAWAXIS_CLASSID, 1);
71   if (--((PetscObject)(*axis))->refct > 0) {
72     *axis = NULL;
73     PetscFunctionReturn(0);
74   }
75 
76   PetscCall(PetscFree((*axis)->toplabel));
77   PetscCall(PetscFree((*axis)->xlabel));
78   PetscCall(PetscFree((*axis)->ylabel));
79   PetscCall(PetscDrawDestroy(&(*axis)->win));
80   PetscCall(PetscHeaderDestroy(axis));
81   PetscFunctionReturn(0);
82 }
83 
84 /*@
85     PetscDrawAxisSetColors -  Sets the colors to be used for the axis,
86                          tickmarks, and text.
87 
88     Logically Collective on axis
89 
90     Input Parameters:
91 +   axis - the axis
92 .   ac - the color of the axis lines
93 .   tc - the color of the tick marks
94 -   cc - the color of the text strings
95 
96     Level: advanced
97 
98 .seealso: `PetscDraw`, `PetscDrawAxisCreate()`, `PetscDrawAxis`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisDraw()`, `PetscDrawAxisSetLimits()`
99 @*/
100 PetscErrorCode PetscDrawAxisSetColors(PetscDrawAxis axis, int ac, int tc, int cc) {
101   PetscFunctionBegin;
102   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
103   PetscValidLogicalCollectiveInt(axis, ac, 2);
104   PetscValidLogicalCollectiveInt(axis, tc, 3);
105   PetscValidLogicalCollectiveInt(axis, cc, 4);
106   axis->ac = ac;
107   axis->tc = tc;
108   axis->cc = cc;
109   PetscFunctionReturn(0);
110 }
111 
112 /*@C
113     PetscDrawAxisSetLabels -  Sets the x and y axis labels.
114 
115     Logically Collective on axis
116 
117     Input Parameters:
118 +   axis - the axis
119 .   top - the label at the top of the image
120 -   xlabel,ylabel - the labes for the x and y axis
121 
122     Notes:
123     Must be called before `PetscDrawAxisDraw()` or `PetscDrawLGDraw()`
124 
125     There should be no newlines in the arguments
126 
127     Level: advanced
128 
129 .seealso: `PetscDraw`, `PetscDrawAxisCreate()`, `PetscDrawAxis`, `PetscDrawAxisSetColors()`, `PetscDrawAxisDraw()`, `PetscDrawAxisSetLimits()`
130 @*/
131 PetscErrorCode PetscDrawAxisSetLabels(PetscDrawAxis axis, const char top[], const char xlabel[], const char ylabel[]) {
132   PetscFunctionBegin;
133   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
134   PetscCall(PetscFree(axis->xlabel));
135   PetscCall(PetscFree(axis->ylabel));
136   PetscCall(PetscFree(axis->toplabel));
137   PetscCall(PetscStrallocpy(xlabel, &axis->xlabel));
138   PetscCall(PetscStrallocpy(ylabel, &axis->ylabel));
139   PetscCall(PetscStrallocpy(top, &axis->toplabel));
140   PetscFunctionReturn(0);
141 }
142 
143 /*@
144     PetscDrawAxisSetLimits -  Sets the limits (in user coords) of the axis
145 
146     Logically Collective on axis
147 
148     Input Parameters:
149 +   axis - the axis
150 .   xmin,xmax - limits in x
151 -   ymin,ymax - limits in y
152 
153     Options Database Key:
154 .   -drawaxis_hold - hold the initial set of axis limits for future plotting
155 
156     Level: advanced
157 
158 .seealso: `PetscDrawAxisSetHoldLimits()`, `PetscDrawAxisGetLimits()`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisSetColors()`
159 @*/
160 PetscErrorCode PetscDrawAxisSetLimits(PetscDrawAxis axis, PetscReal xmin, PetscReal xmax, PetscReal ymin, PetscReal ymax) {
161   PetscFunctionBegin;
162   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
163   if (axis->hold) PetscFunctionReturn(0);
164   axis->xlow  = xmin;
165   axis->xhigh = xmax;
166   axis->ylow  = ymin;
167   axis->yhigh = ymax;
168   PetscCall(PetscOptionsHasName(((PetscObject)axis)->options, ((PetscObject)axis)->prefix, "-drawaxis_hold", &axis->hold));
169   PetscFunctionReturn(0);
170 }
171 
172 /*@
173     PetscDrawAxisGetLimits -  Gets the limits (in user coords) of the axis
174 
175     Not Collective
176 
177     Input Parameters:
178 +   axis - the axis
179 .   xmin,xmax - limits in x
180 -   ymin,ymax - limits in y
181 
182     Level: advanced
183 
184 .seealso: `PetscDrawAxisCreate()`, `PetscDrawAxis`, `PetscDrawAxisSetHoldLimits()`, `PetscDrawAxisSetLimits()`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisSetColors()`
185 @*/
186 PetscErrorCode PetscDrawAxisGetLimits(PetscDrawAxis axis, PetscReal *xmin, PetscReal *xmax, PetscReal *ymin, PetscReal *ymax) {
187   PetscFunctionBegin;
188   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
189   if (xmin) *xmin = axis->xlow;
190   if (xmax) *xmax = axis->xhigh;
191   if (ymin) *ymin = axis->ylow;
192   if (ymax) *ymax = axis->yhigh;
193   PetscFunctionReturn(0);
194 }
195 
196 /*@
197     PetscDrawAxisSetHoldLimits -  Causes an axis to keep the same limits until this is called
198         again
199 
200     Logically Collective on axis
201 
202     Input Parameters:
203 +   axis - the axis
204 -   hold - `PETSC_TRUE` - hold current limits, `PETSC_FALSE` allow limits to be changed
205 
206     Level: advanced
207 
208     Note:
209         Once this has been called with `PETSC_TRUE` the limits will not change if you call
210      `PetscDrawAxisSetLimits()` until you call this with `PETSC_FALSE`
211 
212 .seealso: `PetscDrawAxisCreate()`, `PetscDrawAxis`, `PetscDrawAxisGetLimits()`, `PetscDrawAxisSetLimits()`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisSetColors()`
213 @*/
214 PetscErrorCode PetscDrawAxisSetHoldLimits(PetscDrawAxis axis, PetscBool hold) {
215   PetscFunctionBegin;
216   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
217   PetscValidLogicalCollectiveBool(axis, hold, 2);
218   axis->hold = hold;
219   PetscFunctionReturn(0);
220 }
221 
222 /*@
223     PetscDrawAxisDraw - draws an axis.
224 
225     Collective on axis
226 
227     Input Parameter:
228 .   axis - `PetscDrawAxis` structure
229 
230     Level: advanced
231 
232     Note:
233     This draws the actual axis.  The limits etc have already been set.
234     By picking special routines for the ticks and labels, special
235     effects may be generated.  These routines are part of the Axis
236     structure (axis).
237 
238 .seealso: `PetscDrawAxisCreate()`, `PetscDrawAxis`, `PetscDrawAxisGetLimits()`, `PetscDrawAxisSetLimits()`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisSetColors()`
239 @*/
240 PetscErrorCode PetscDrawAxisDraw(PetscDrawAxis axis) {
241   int         i, ntick, numx, numy, ac, tc, cc;
242   PetscMPIInt rank;
243   size_t      len, ytlen = 0;
244   PetscReal   coors[4], tickloc[PETSC_DRAW_AXIS_MAX_SEGMENTS], sep, tw, th;
245   PetscReal   xl, xr, yl, yr, dxl = 0, dyl = 0, dxr = 0, dyr = 0;
246   char       *p;
247   PetscDraw   draw;
248   PetscBool   isnull;
249 
250   PetscFunctionBegin;
251   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
252   PetscCall(PetscDrawIsNull(axis->win, &isnull));
253   if (isnull) PetscFunctionReturn(0);
254   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)axis), &rank));
255 
256   draw = axis->win;
257 
258   ac = axis->ac;
259   tc = axis->tc;
260   cc = axis->cc;
261   if (axis->xlow == axis->xhigh) {
262     axis->xlow -= .5;
263     axis->xhigh += .5;
264   }
265   if (axis->ylow == axis->yhigh) {
266     axis->ylow -= .5;
267     axis->yhigh += .5;
268   }
269 
270   PetscDrawCollectiveBegin(draw);
271   if (rank) goto finally;
272 
273   /* get cannonical string size */
274   PetscCall(PetscDrawSetCoordinates(draw, 0, 0, 1, 1));
275   PetscCall(PetscDrawStringGetSize(draw, &tw, &th));
276   /* lower spacing */
277   if (axis->xlabelstr) dyl += 1.5 * th;
278   if (axis->xlabel) dyl += 1.5 * th;
279   /* left spacing */
280   if (axis->ylabelstr) dxl += 7.5 * tw;
281   if (axis->ylabel) dxl += 2.0 * tw;
282   /* right and top spacing */
283   if (axis->xlabelstr) dxr = 2.5 * tw;
284   if (axis->ylabelstr) dyr = 0.5 * th;
285   if (axis->toplabel) dyr = 1.5 * th;
286   /* extra spacing */
287   dxl += 0.7 * tw;
288   dxr += 0.5 * tw;
289   dyl += 0.2 * th;
290   dyr += 0.2 * th;
291   /* determine coordinates */
292   xl = (dxl * axis->xhigh + dxr * axis->xlow - axis->xlow) / (dxl + dxr - 1);
293   xr = (dxl * axis->xhigh + dxr * axis->xlow - axis->xhigh) / (dxl + dxr - 1);
294   yl = (dyl * axis->yhigh + dyr * axis->ylow - axis->ylow) / (dyl + dyr - 1);
295   yr = (dyl * axis->yhigh + dyr * axis->ylow - axis->yhigh) / (dyl + dyr - 1);
296   PetscCall(PetscDrawSetCoordinates(draw, xl, yl, xr, yr));
297   PetscCall(PetscDrawStringGetSize(draw, &tw, &th));
298 
299   /* PetscDraw the axis lines */
300   PetscCall(PetscDrawLine(draw, axis->xlow, axis->ylow, axis->xhigh, axis->ylow, ac));
301   PetscCall(PetscDrawLine(draw, axis->xlow, axis->ylow, axis->xlow, axis->yhigh, ac));
302   PetscCall(PetscDrawLine(draw, axis->xlow, axis->yhigh, axis->xhigh, axis->yhigh, ac));
303   PetscCall(PetscDrawLine(draw, axis->xhigh, axis->ylow, axis->xhigh, axis->yhigh, ac));
304 
305   /* PetscDraw the top label */
306   if (axis->toplabel) {
307     PetscReal x = (axis->xlow + axis->xhigh) / 2, y = axis->yhigh + 0.5 * th;
308     PetscCall(PetscDrawStringCentered(draw, x, y, cc, axis->toplabel));
309   }
310 
311   /* PetscDraw the X ticks and labels */
312   if (axis->xticks) {
313     numx = (int)(.15 * (axis->xhigh - axis->xlow) / tw);
314     numx = PetscClipInterval(numx, 2, 6);
315     PetscCall((*axis->xticks)(axis->xlow, axis->xhigh, numx, &ntick, tickloc, PETSC_DRAW_AXIS_MAX_SEGMENTS));
316     /* PetscDraw in tick marks */
317     for (i = 0; i < ntick; i++) {
318       PetscCall(PetscDrawLine(draw, tickloc[i], axis->ylow, tickloc[i], axis->ylow + .5 * th, tc));
319       PetscCall(PetscDrawLine(draw, tickloc[i], axis->yhigh, tickloc[i], axis->yhigh - .5 * th, tc));
320     }
321     /* label ticks */
322     if (axis->xlabelstr) {
323       for (i = 0; i < ntick; i++) {
324         if (i < ntick - 1) sep = tickloc[i + 1] - tickloc[i];
325         else if (i > 0) sep = tickloc[i] - tickloc[i - 1];
326         else sep = 0.0;
327         PetscCall((*axis->xlabelstr)(tickloc[i], sep, &p));
328         PetscCall(PetscDrawStringCentered(draw, tickloc[i], axis->ylow - 1.5 * th, cc, p));
329       }
330     }
331   }
332   if (axis->xlabel) {
333     PetscReal x = (axis->xlow + axis->xhigh) / 2, y = axis->ylow - 1.5 * th;
334     if (axis->xlabelstr) y -= 1.5 * th;
335     PetscCall(PetscDrawStringCentered(draw, x, y, cc, axis->xlabel));
336   }
337 
338   /* PetscDraw the Y ticks and labels */
339   if (axis->yticks) {
340     numy = (int)(.50 * (axis->yhigh - axis->ylow) / th);
341     numy = PetscClipInterval(numy, 2, 6);
342     PetscCall((*axis->yticks)(axis->ylow, axis->yhigh, numy, &ntick, tickloc, PETSC_DRAW_AXIS_MAX_SEGMENTS));
343     /* PetscDraw in tick marks */
344     for (i = 0; i < ntick; i++) {
345       PetscCall(PetscDrawLine(draw, axis->xlow, tickloc[i], axis->xlow + .5 * tw, tickloc[i], tc));
346       PetscCall(PetscDrawLine(draw, axis->xhigh, tickloc[i], axis->xhigh - .5 * tw, tickloc[i], tc));
347     }
348     /* label ticks */
349     if (axis->ylabelstr) {
350       for (i = 0; i < ntick; i++) {
351         if (i < ntick - 1) sep = tickloc[i + 1] - tickloc[i];
352         else if (i > 0) sep = tickloc[i] - tickloc[i - 1];
353         else sep = 0.0;
354         PetscCall((*axis->ylabelstr)(tickloc[i], sep, &p));
355         PetscCall(PetscStrlen(p, &len));
356         ytlen = PetscMax(ytlen, len);
357         PetscCall(PetscDrawString(draw, axis->xlow - (len + .5) * tw, tickloc[i] - .5 * th, cc, p));
358       }
359     }
360   }
361   if (axis->ylabel) {
362     PetscReal x = axis->xlow - 2.0 * tw, y = (axis->ylow + axis->yhigh) / 2;
363     if (axis->ylabelstr) x -= (ytlen + .5) * tw;
364     PetscCall(PetscStrlen(axis->ylabel, &len));
365     PetscCall(PetscDrawStringVertical(draw, x, y + len * th / 2, cc, axis->ylabel));
366   }
367 
368   PetscCall(PetscDrawGetCoordinates(draw, &coors[0], &coors[1], &coors[2], &coors[3]));
369 finally:
370   PetscDrawCollectiveEnd(draw);
371   PetscCallMPI(MPI_Bcast(coors, 4, MPIU_REAL, 0, PetscObjectComm((PetscObject)draw)));
372   PetscCall(PetscDrawSetCoordinates(draw, coors[0], coors[1], coors[2], coors[3]));
373   PetscFunctionReturn(0);
374 }
375 
376 /*
377     Removes all zeros but one from .0000
378 */
379 PetscErrorCode PetscStripe0(char *buf) {
380   size_t    n;
381   PetscBool flg;
382   char     *str;
383 
384   PetscFunctionBegin;
385   PetscCall(PetscStrlen(buf, &n));
386   PetscCall(PetscStrendswith(buf, "e00", &flg));
387   if (flg) buf[n - 3] = 0;
388   PetscCall(PetscStrstr(buf, "e0", &str));
389   if (str) {
390     buf[n - 2] = buf[n - 1];
391     buf[n - 1] = 0;
392   }
393   PetscCall(PetscStrstr(buf, "e-0", &str));
394   if (str) {
395     buf[n - 2] = buf[n - 1];
396     buf[n - 1] = 0;
397   }
398   PetscFunctionReturn(0);
399 }
400 
401 /*
402     Removes all zeros but one from .0000
403 */
404 PetscErrorCode PetscStripAllZeros(char *buf) {
405   size_t i, n;
406 
407   PetscFunctionBegin;
408   PetscCall(PetscStrlen(buf, &n));
409   if (buf[0] != '.') PetscFunctionReturn(0);
410   for (i = 1; i < n; i++) {
411     if (buf[i] != '0') PetscFunctionReturn(0);
412   }
413   buf[0] = '0';
414   buf[1] = 0;
415   PetscFunctionReturn(0);
416 }
417 
418 /*
419     Removes trailing zeros
420 */
421 PetscErrorCode PetscStripTrailingZeros(char *buf) {
422   char  *found;
423   size_t i, n, m = PETSC_MAX_INT;
424 
425   PetscFunctionBegin;
426   /* if there is an e in string DO NOT strip trailing zeros */
427   PetscCall(PetscStrchr(buf, 'e', &found));
428   if (found) PetscFunctionReturn(0);
429 
430   PetscCall(PetscStrlen(buf, &n));
431   /* locate decimal point */
432   for (i = 0; i < n; i++) {
433     if (buf[i] == '.') {
434       m = i;
435       break;
436     }
437   }
438   /* if not decimal point then no zeros to remove */
439   if (m == PETSC_MAX_INT) PetscFunctionReturn(0);
440   /* start at right end of string removing 0s */
441   for (i = n - 1; i > m; i++) {
442     if (buf[i] != '0') PetscFunctionReturn(0);
443     buf[i] = 0;
444   }
445   PetscFunctionReturn(0);
446 }
447 
448 /*
449     Removes leading 0 from 0.22 or -0.22
450 */
451 PetscErrorCode PetscStripInitialZero(char *buf) {
452   size_t i, n;
453 
454   PetscFunctionBegin;
455   PetscCall(PetscStrlen(buf, &n));
456   if (buf[0] == '0') {
457     for (i = 0; i < n; i++) buf[i] = buf[i + 1];
458   } else if (buf[0] == '-' && buf[1] == '0') {
459     for (i = 1; i < n; i++) buf[i] = buf[i + 1];
460   }
461   PetscFunctionReturn(0);
462 }
463 
464 /*
465      Removes the extraneous zeros in numbers like 1.10000e6
466 */
467 PetscErrorCode PetscStripZeros(char *buf) {
468   size_t i, j, n;
469 
470   PetscFunctionBegin;
471   PetscCall(PetscStrlen(buf, &n));
472   if (n < 5) PetscFunctionReturn(0);
473   for (i = 1; i < n - 1; i++) {
474     if (buf[i] == 'e' && buf[i - 1] == '0') {
475       for (j = i; j < n + 1; j++) buf[j - 1] = buf[j];
476       PetscCall(PetscStripZeros(buf));
477       PetscFunctionReturn(0);
478     }
479   }
480   PetscFunctionReturn(0);
481 }
482 
483 /*
484       Removes the plus in something like 1.1e+2 or 1.1e+02
485 */
486 PetscErrorCode PetscStripZerosPlus(char *buf) {
487   size_t i, j, n;
488 
489   PetscFunctionBegin;
490   PetscCall(PetscStrlen(buf, &n));
491   if (n < 5) PetscFunctionReturn(0);
492   for (i = 1; i < n - 2; i++) {
493     if (buf[i] == '+') {
494       if (buf[i + 1] == '0') {
495         for (j = i + 1; j < n; j++) buf[j - 1] = buf[j + 1];
496         PetscFunctionReturn(0);
497       } else {
498         for (j = i + 1; j < n + 1; j++) buf[j - 1] = buf[j];
499         PetscFunctionReturn(0);
500       }
501     } else if (buf[i] == '-') {
502       if (buf[i + 1] == '0') {
503         for (j = i + 1; j < n; j++) buf[j] = buf[j + 1];
504         PetscFunctionReturn(0);
505       }
506     }
507   }
508   PetscFunctionReturn(0);
509 }
510