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