xref: /petsc/src/sys/classes/draw/utils/axisc.c (revision a69119a591a03a9d906b29c0a4e9802e4d7c9795)
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 PetscDraw
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    Notes:
18     the MPI communicator that owns the underlying draw object owns the PetscDrawAxis object, but calls to set PetscDrawAxis options are ignored by all processes
19           except the first MPI process 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 PetscDrawAxis
59 
60     Input Parameters:
61 .   axis - the axis context
62 
63     Level: advanced
64 
65 .seealso: `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 PetscDrawAxis
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: `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 PetscDrawAxis
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            There should be no newlines in the arguments
125 
126     Level: advanced
127 
128 .seealso: `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 PetscDrawAxis
146 
147     Input Parameters:
148 +   axis - the axis
149 .   xmin,xmax - limits in x
150 -   ymin,ymax - limits in y
151 
152     Options Database:
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 @*/
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 @*/
187 PetscErrorCode PetscDrawAxisGetLimits(PetscDrawAxis axis, PetscReal *xmin, PetscReal *xmax, PetscReal *ymin, PetscReal *ymax) {
188   PetscFunctionBegin;
189   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
190   if (xmin) *xmin = axis->xlow;
191   if (xmax) *xmax = axis->xhigh;
192   if (ymin) *ymin = axis->ylow;
193   if (ymax) *ymax = axis->yhigh;
194   PetscFunctionReturn(0);
195 }
196 
197 /*@
198     PetscDrawAxisSetHoldLimits -  Causes an axis to keep the same limits until this is called
199         again
200 
201     Logically Collective on PetscDrawAxis
202 
203     Input Parameters:
204 +   axis - the axis
205 -   hold - PETSC_TRUE - hold current limits, PETSC_FALSE allow limits to be changed
206 
207     Level: advanced
208 
209     Notes:
210         Once this has been called with PETSC_TRUE the limits will not change if you call
211      PetscDrawAxisSetLimits() until you call this with PETSC_FALSE
212 
213 .seealso: `PetscDrawAxisCreate()`, `PetscDrawAxis`, `PetscDrawAxisGetLimits()`, `PetscDrawAxisSetLimits()`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisSetColors()`
214 
215 @*/
216 PetscErrorCode PetscDrawAxisSetHoldLimits(PetscDrawAxis axis, PetscBool hold) {
217   PetscFunctionBegin;
218   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
219   PetscValidLogicalCollectiveBool(axis, hold, 2);
220   axis->hold = hold;
221   PetscFunctionReturn(0);
222 }
223 
224 /*@
225     PetscDrawAxisDraw - PetscDraws an axis.
226 
227     Collective on PetscDrawAxis
228 
229     Input Parameter:
230 .   axis - Axis structure
231 
232     Level: advanced
233 
234     Note:
235     This draws the actual axis.  The limits etc have already been set.
236     By picking special routines for the ticks and labels, special
237     effects may be generated.  These routines are part of the Axis
238     structure (axis).
239 
240 .seealso: `PetscDrawAxisCreate()`, `PetscDrawAxis`, `PetscDrawAxisGetLimits()`, `PetscDrawAxisSetLimits()`, `PetscDrawAxisSetLabels()`, `PetscDrawAxisSetColors()`
241 
242 @*/
243 PetscErrorCode PetscDrawAxisDraw(PetscDrawAxis axis) {
244   int         i, ntick, numx, numy, ac, tc, cc;
245   PetscMPIInt rank;
246   size_t      len, ytlen = 0;
247   PetscReal   coors[4], tickloc[PETSC_DRAW_AXIS_MAX_SEGMENTS], sep, tw, th;
248   PetscReal   xl, xr, yl, yr, dxl = 0, dyl = 0, dxr = 0, dyr = 0;
249   char       *p;
250   PetscDraw   draw;
251   PetscBool   isnull;
252 
253   PetscFunctionBegin;
254   PetscValidHeaderSpecific(axis, PETSC_DRAWAXIS_CLASSID, 1);
255   PetscCall(PetscDrawIsNull(axis->win, &isnull));
256   if (isnull) PetscFunctionReturn(0);
257   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)axis), &rank));
258 
259   draw = axis->win;
260 
261   ac = axis->ac;
262   tc = axis->tc;
263   cc = axis->cc;
264   if (axis->xlow == axis->xhigh) {
265     axis->xlow -= .5;
266     axis->xhigh += .5;
267   }
268   if (axis->ylow == axis->yhigh) {
269     axis->ylow -= .5;
270     axis->yhigh += .5;
271   }
272 
273   PetscDrawCollectiveBegin(draw);
274   if (rank) goto finally;
275 
276   /* get cannonical string size */
277   PetscCall(PetscDrawSetCoordinates(draw, 0, 0, 1, 1));
278   PetscCall(PetscDrawStringGetSize(draw, &tw, &th));
279   /* lower spacing */
280   if (axis->xlabelstr) dyl += 1.5 * th;
281   if (axis->xlabel) dyl += 1.5 * th;
282   /* left spacing */
283   if (axis->ylabelstr) dxl += 7.5 * tw;
284   if (axis->ylabel) dxl += 2.0 * tw;
285   /* right and top spacing */
286   if (axis->xlabelstr) dxr = 2.5 * tw;
287   if (axis->ylabelstr) dyr = 0.5 * th;
288   if (axis->toplabel) dyr = 1.5 * th;
289   /* extra spacing */
290   dxl += 0.7 * tw;
291   dxr += 0.5 * tw;
292   dyl += 0.2 * th;
293   dyr += 0.2 * th;
294   /* determine coordinates */
295   xl = (dxl * axis->xhigh + dxr * axis->xlow - axis->xlow) / (dxl + dxr - 1);
296   xr = (dxl * axis->xhigh + dxr * axis->xlow - axis->xhigh) / (dxl + dxr - 1);
297   yl = (dyl * axis->yhigh + dyr * axis->ylow - axis->ylow) / (dyl + dyr - 1);
298   yr = (dyl * axis->yhigh + dyr * axis->ylow - axis->yhigh) / (dyl + dyr - 1);
299   PetscCall(PetscDrawSetCoordinates(draw, xl, yl, xr, yr));
300   PetscCall(PetscDrawStringGetSize(draw, &tw, &th));
301 
302   /* PetscDraw the axis lines */
303   PetscCall(PetscDrawLine(draw, axis->xlow, axis->ylow, axis->xhigh, axis->ylow, ac));
304   PetscCall(PetscDrawLine(draw, axis->xlow, axis->ylow, axis->xlow, axis->yhigh, ac));
305   PetscCall(PetscDrawLine(draw, axis->xlow, axis->yhigh, axis->xhigh, axis->yhigh, ac));
306   PetscCall(PetscDrawLine(draw, axis->xhigh, axis->ylow, axis->xhigh, axis->yhigh, ac));
307 
308   /* PetscDraw the top label */
309   if (axis->toplabel) {
310     PetscReal x = (axis->xlow + axis->xhigh) / 2, y = axis->yhigh + 0.5 * th;
311     PetscCall(PetscDrawStringCentered(draw, x, y, cc, axis->toplabel));
312   }
313 
314   /* PetscDraw the X ticks and labels */
315   if (axis->xticks) {
316     numx = (int)(.15 * (axis->xhigh - axis->xlow) / tw);
317     numx = PetscClipInterval(numx, 2, 6);
318     PetscCall((*axis->xticks)(axis->xlow, axis->xhigh, numx, &ntick, tickloc, PETSC_DRAW_AXIS_MAX_SEGMENTS));
319     /* PetscDraw in tick marks */
320     for (i = 0; i < ntick; i++) {
321       PetscCall(PetscDrawLine(draw, tickloc[i], axis->ylow, tickloc[i], axis->ylow + .5 * th, tc));
322       PetscCall(PetscDrawLine(draw, tickloc[i], axis->yhigh, tickloc[i], axis->yhigh - .5 * th, tc));
323     }
324     /* label ticks */
325     if (axis->xlabelstr) {
326       for (i = 0; i < ntick; i++) {
327         if (i < ntick - 1) sep = tickloc[i + 1] - tickloc[i];
328         else if (i > 0) sep = tickloc[i] - tickloc[i - 1];
329         else sep = 0.0;
330         PetscCall((*axis->xlabelstr)(tickloc[i], sep, &p));
331         PetscCall(PetscDrawStringCentered(draw, tickloc[i], axis->ylow - 1.5 * th, cc, p));
332       }
333     }
334   }
335   if (axis->xlabel) {
336     PetscReal x = (axis->xlow + axis->xhigh) / 2, y = axis->ylow - 1.5 * th;
337     if (axis->xlabelstr) y -= 1.5 * th;
338     PetscCall(PetscDrawStringCentered(draw, x, y, cc, axis->xlabel));
339   }
340 
341   /* PetscDraw the Y ticks and labels */
342   if (axis->yticks) {
343     numy = (int)(.50 * (axis->yhigh - axis->ylow) / th);
344     numy = PetscClipInterval(numy, 2, 6);
345     PetscCall((*axis->yticks)(axis->ylow, axis->yhigh, numy, &ntick, tickloc, PETSC_DRAW_AXIS_MAX_SEGMENTS));
346     /* PetscDraw in tick marks */
347     for (i = 0; i < ntick; i++) {
348       PetscCall(PetscDrawLine(draw, axis->xlow, tickloc[i], axis->xlow + .5 * tw, tickloc[i], tc));
349       PetscCall(PetscDrawLine(draw, axis->xhigh, tickloc[i], axis->xhigh - .5 * tw, tickloc[i], tc));
350     }
351     /* label ticks */
352     if (axis->ylabelstr) {
353       for (i = 0; i < ntick; i++) {
354         if (i < ntick - 1) sep = tickloc[i + 1] - tickloc[i];
355         else if (i > 0) sep = tickloc[i] - tickloc[i - 1];
356         else sep = 0.0;
357         PetscCall((*axis->ylabelstr)(tickloc[i], sep, &p));
358         PetscCall(PetscStrlen(p, &len));
359         ytlen = PetscMax(ytlen, len);
360         PetscCall(PetscDrawString(draw, axis->xlow - (len + .5) * tw, tickloc[i] - .5 * th, cc, p));
361       }
362     }
363   }
364   if (axis->ylabel) {
365     PetscReal x = axis->xlow - 2.0 * tw, y = (axis->ylow + axis->yhigh) / 2;
366     if (axis->ylabelstr) x -= (ytlen + .5) * tw;
367     PetscCall(PetscStrlen(axis->ylabel, &len));
368     PetscCall(PetscDrawStringVertical(draw, x, y + len * th / 2, cc, axis->ylabel));
369   }
370 
371   PetscCall(PetscDrawGetCoordinates(draw, &coors[0], &coors[1], &coors[2], &coors[3]));
372 finally:
373   PetscDrawCollectiveEnd(draw);
374   PetscCallMPI(MPI_Bcast(coors, 4, MPIU_REAL, 0, PetscObjectComm((PetscObject)draw)));
375   PetscCall(PetscDrawSetCoordinates(draw, coors[0], coors[1], coors[2], coors[3]));
376   PetscFunctionReturn(0);
377 }
378 
379 /*
380     Removes all zeros but one from .0000
381 */
382 PetscErrorCode PetscStripe0(char *buf) {
383   size_t    n;
384   PetscBool flg;
385   char     *str;
386 
387   PetscFunctionBegin;
388   PetscCall(PetscStrlen(buf, &n));
389   PetscCall(PetscStrendswith(buf, "e00", &flg));
390   if (flg) buf[n - 3] = 0;
391   PetscCall(PetscStrstr(buf, "e0", &str));
392   if (str) {
393     buf[n - 2] = buf[n - 1];
394     buf[n - 1] = 0;
395   }
396   PetscCall(PetscStrstr(buf, "e-0", &str));
397   if (str) {
398     buf[n - 2] = buf[n - 1];
399     buf[n - 1] = 0;
400   }
401   PetscFunctionReturn(0);
402 }
403 
404 /*
405     Removes all zeros but one from .0000
406 */
407 PetscErrorCode PetscStripAllZeros(char *buf) {
408   size_t i, n;
409 
410   PetscFunctionBegin;
411   PetscCall(PetscStrlen(buf, &n));
412   if (buf[0] != '.') PetscFunctionReturn(0);
413   for (i = 1; i < n; i++) {
414     if (buf[i] != '0') PetscFunctionReturn(0);
415   }
416   buf[0] = '0';
417   buf[1] = 0;
418   PetscFunctionReturn(0);
419 }
420 
421 /*
422     Removes trailing zeros
423 */
424 PetscErrorCode PetscStripTrailingZeros(char *buf) {
425   char  *found;
426   size_t i, n, m = PETSC_MAX_INT;
427 
428   PetscFunctionBegin;
429   /* if there is an e in string DO NOT strip trailing zeros */
430   PetscCall(PetscStrchr(buf, 'e', &found));
431   if (found) PetscFunctionReturn(0);
432 
433   PetscCall(PetscStrlen(buf, &n));
434   /* locate decimal point */
435   for (i = 0; i < n; i++) {
436     if (buf[i] == '.') {
437       m = i;
438       break;
439     }
440   }
441   /* if not decimal point then no zeros to remove */
442   if (m == PETSC_MAX_INT) PetscFunctionReturn(0);
443   /* start at right end of string removing 0s */
444   for (i = n - 1; i > m; i++) {
445     if (buf[i] != '0') PetscFunctionReturn(0);
446     buf[i] = 0;
447   }
448   PetscFunctionReturn(0);
449 }
450 
451 /*
452     Removes leading 0 from 0.22 or -0.22
453 */
454 PetscErrorCode PetscStripInitialZero(char *buf) {
455   size_t i, n;
456 
457   PetscFunctionBegin;
458   PetscCall(PetscStrlen(buf, &n));
459   if (buf[0] == '0') {
460     for (i = 0; i < n; i++) buf[i] = buf[i + 1];
461   } else if (buf[0] == '-' && buf[1] == '0') {
462     for (i = 1; i < n; i++) buf[i] = buf[i + 1];
463   }
464   PetscFunctionReturn(0);
465 }
466 
467 /*
468      Removes the extraneous zeros in numbers like 1.10000e6
469 */
470 PetscErrorCode PetscStripZeros(char *buf) {
471   size_t i, j, n;
472 
473   PetscFunctionBegin;
474   PetscCall(PetscStrlen(buf, &n));
475   if (n < 5) PetscFunctionReturn(0);
476   for (i = 1; i < n - 1; i++) {
477     if (buf[i] == 'e' && buf[i - 1] == '0') {
478       for (j = i; j < n + 1; j++) buf[j - 1] = buf[j];
479       PetscCall(PetscStripZeros(buf));
480       PetscFunctionReturn(0);
481     }
482   }
483   PetscFunctionReturn(0);
484 }
485 
486 /*
487       Removes the plus in something like 1.1e+2 or 1.1e+02
488 */
489 PetscErrorCode PetscStripZerosPlus(char *buf) {
490   size_t i, j, n;
491 
492   PetscFunctionBegin;
493   PetscCall(PetscStrlen(buf, &n));
494   if (n < 5) PetscFunctionReturn(0);
495   for (i = 1; i < n - 2; i++) {
496     if (buf[i] == '+') {
497       if (buf[i + 1] == '0') {
498         for (j = i + 1; j < n; j++) buf[j - 1] = buf[j + 1];
499         PetscFunctionReturn(0);
500       } else {
501         for (j = i + 1; j < n + 1; j++) buf[j - 1] = buf[j];
502         PetscFunctionReturn(0);
503       }
504     } else if (buf[i] == '-') {
505       if (buf[i + 1] == '0') {
506         for (j = i + 1; j < n; j++) buf[j] = buf[j + 1];
507         PetscFunctionReturn(0);
508       }
509     }
510   }
511   PetscFunctionReturn(0);
512 }
513