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