xref: /petsc/src/sys/classes/draw/utils/bars.c (revision dc97da86830806a16cfaacfab740baa2e54c8135)
1 /*
2   Contains the data structure for plotting a bargraph in a window with an axis.
3 */
4 
5 #include <petsc/private/drawimpl.h> /*I "petscdraw.h" I*/
6 #include <petscviewer.h>            /*I "petscviewer.h" I*/
7 
8 PetscClassId PETSC_DRAWBAR_CLASSID = 0;
9 
10 /*@
11   PetscDrawBarCreate - Creates a bar graph data structure.
12 
13   Collective
14 
15   Input Parameter:
16 . draw - The window where the graph will be made
17 
18   Output Parameter:
19 . bar - The bar graph context
20 
21   Notes:
22   Call `PetscDrawBarSetData()` to provide the bins to be plotted and then `PetscDrawBarDraw()` to display the new plot
23 
24   The difference between a bar chart, `PetscDrawBar`, and a histogram, `PetscDrawHG`, is explained here <https://stattrek.com/statistics/charts/histogram.aspx?Tutorial=AP>
25 
26   The MPI communicator that owns the `PetscDraw` owns this `PetscDrawBar`, but the calls to set options and add data are ignored on all processes except the
27   zeroth MPI process in the communicator. All MPI processes in the communicator must call `PetscDrawBarDraw()` to display the updated graph.
28 
29   Level: intermediate
30 
31 .seealso: `PetscDrawBar`, `PetscDrawLGCreate()`, `PetscDrawLG`, `PetscDrawSPCreate()`, `PetscDrawSP`, `PetscDrawHGCreate()`, `PetscDrawHG`, `PetscDrawBarDestroy()`, `PetscDrawBarSetData()`,
32           `PetscDrawBarDraw()`, `PetscDrawBarSave()`, `PetscDrawBarSetColor()`, `PetscDrawBarSort()`, `PetscDrawBarSetLimits()`, `PetscDrawBarGetAxis()`, `PetscDrawAxis`,
33           `PetscDrawBarGetDraw()`, `PetscDrawBarSetFromOptions()`
34 @*/
35 PetscErrorCode PetscDrawBarCreate(PetscDraw draw, PetscDrawBar *bar)
36 {
37   PetscDrawBar h;
38 
39   PetscFunctionBegin;
40   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
41   PetscAssertPointer(bar, 2);
42 
43   PetscCall(PetscHeaderCreate(h, PETSC_DRAWBAR_CLASSID, "DrawBar", "Bar Graph", "Draw", PetscObjectComm((PetscObject)draw), PetscDrawBarDestroy, NULL));
44   PetscCall(PetscObjectReference((PetscObject)draw));
45   h->win     = draw;
46   h->view    = NULL;
47   h->destroy = NULL;
48   h->color   = PETSC_DRAW_GREEN;
49   h->ymin    = 0.; /* if user has not set these then they are determined from the data */
50   h->ymax    = 0.;
51   h->numBins = 0;
52   PetscCall(PetscDrawAxisCreate(draw, &h->axis));
53   h->axis->xticks = NULL;
54   *bar            = h;
55   PetscFunctionReturn(PETSC_SUCCESS);
56 }
57 
58 /*@C
59   PetscDrawBarSetData - Set the data for a bar graph
60 
61   Logically Collective
62 
63   Input Parameters:
64 + bar    - The bar graph context.
65 . bins   - number of items
66 . data   - values of each item
67 - labels - optional label for each bar, `NULL` terminated array of strings
68 
69   Level: intermediate
70 
71   Notes:
72   Call `PetscDrawBarDraw()` after this call to display the new plot
73 
74   The data is ignored on all MPI processes except rank zero
75 
76 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
77 @*/
78 PetscErrorCode PetscDrawBarSetData(PetscDrawBar bar, PetscInt bins, const PetscReal data[], const char *const labels[])
79 {
80   PetscFunctionBegin;
81   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
82 
83   if (bar->numBins != bins) {
84     PetscCall(PetscFree(bar->values));
85     PetscCall(PetscMalloc1(bins, &bar->values));
86     bar->numBins = (int)bins;
87   }
88   PetscCall(PetscArraycpy(bar->values, data, bins));
89   bar->numBins = (int)bins;
90   if (labels) PetscCall(PetscStrArrayallocpy(labels, &bar->labels));
91   PetscFunctionReturn(PETSC_SUCCESS);
92 }
93 
94 /*@
95   PetscDrawBarDestroy - Frees all space taken up by bar graph data structure.
96 
97   Collective
98 
99   Input Parameter:
100 . bar - The bar graph context
101 
102   Level: intermediate
103 
104 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`
105 @*/
106 PetscErrorCode PetscDrawBarDestroy(PetscDrawBar *bar)
107 {
108   PetscFunctionBegin;
109   if (!*bar) PetscFunctionReturn(PETSC_SUCCESS);
110   PetscValidHeaderSpecific(*bar, PETSC_DRAWBAR_CLASSID, 1);
111   if (--((PetscObject)*bar)->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
112 
113   PetscCall(PetscFree((*bar)->values));
114   PetscCall(PetscStrArrayDestroy(&(*bar)->labels));
115   PetscCall(PetscDrawAxisDestroy(&(*bar)->axis));
116   PetscCall(PetscDrawDestroy(&(*bar)->win));
117   PetscCall(PetscHeaderDestroy(bar));
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 /*@
122   PetscDrawBarDraw - Redraws a bar graph.
123 
124   Collective
125 
126   Input Parameter:
127 . bar - The bar graph context
128 
129   Level: intermediate
130 
131 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`
132 @*/
133 PetscErrorCode PetscDrawBarDraw(PetscDrawBar bar)
134 {
135   PetscDraw   draw;
136   PetscBool   isnull;
137   PetscReal   xmin, xmax, ymin, ymax, *values, binLeft, binRight;
138   PetscInt    numValues, i, idx, *perm, nplot;
139   PetscMPIInt rank;
140   char      **labels;
141   int         bcolor, color;
142 
143   PetscFunctionBegin;
144   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
145   PetscCall(PetscDrawIsNull(bar->win, &isnull));
146   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
147   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)bar), &rank));
148 
149   if (bar->numBins < 1) PetscFunctionReturn(PETSC_SUCCESS);
150 
151   color = bar->color;
152   if (color == PETSC_DRAW_ROTATE) bcolor = PETSC_DRAW_BLACK + 1;
153   else bcolor = color;
154 
155   numValues = bar->numBins;
156   values    = bar->values;
157   if (bar->ymin == bar->ymax) {
158     /* user has not set bounds on bars so set them based on the data */
159     ymin = PETSC_MAX_REAL;
160     ymax = PETSC_MIN_REAL;
161     for (i = 0; i < numValues; i++) {
162       ymin = PetscMin(ymin, values[i]);
163       ymax = PetscMax(ymax, values[i]);
164     }
165   } else {
166     ymin = bar->ymin;
167     ymax = bar->ymax;
168   }
169   nplot  = numValues; /* number of points to actually plot; if some are lower than requested tolerance */
170   xmin   = 0.0;
171   xmax   = (PetscReal)nplot;
172   labels = bar->labels;
173 
174   if (bar->sort) {
175     PetscCall(PetscMalloc1(numValues, &perm));
176     for (i = 0; i < numValues; i++) perm[i] = i;
177     PetscCall(PetscSortRealWithPermutation(numValues, values, perm));
178     if (bar->sorttolerance) {
179       for (i = 0; i < numValues; i++) {
180         if (values[perm[numValues - i - 1]] < bar->sorttolerance) {
181           nplot = i;
182           break;
183         }
184       }
185     }
186   }
187 
188   draw = bar->win;
189   PetscCall(PetscDrawCheckResizedWindow(draw));
190   PetscCall(PetscDrawClear(draw));
191 
192   PetscCall(PetscDrawAxisSetLimits(bar->axis, xmin, xmax, ymin, ymax));
193   PetscCall(PetscDrawAxisDraw(bar->axis));
194 
195   PetscDrawCollectiveBegin(draw);
196   if (rank == 0) { /* Draw bins */
197     for (i = 0; i < nplot; i++) {
198       idx      = (bar->sort ? perm[numValues - i - 1] : i);
199       binLeft  = xmin + (PetscReal)i;
200       binRight = xmin + (PetscReal)i + 1;
201       PetscCall(PetscDrawRectangle(draw, binLeft, ymin, binRight, values[idx], bcolor, bcolor, bcolor, bcolor));
202       PetscCall(PetscDrawLine(draw, binLeft, ymin, binLeft, values[idx], PETSC_DRAW_BLACK));
203       PetscCall(PetscDrawLine(draw, binRight, ymin, binRight, values[idx], PETSC_DRAW_BLACK));
204       PetscCall(PetscDrawLine(draw, binLeft, values[idx], binRight, values[idx], PETSC_DRAW_BLACK));
205       if (labels) {
206         PetscReal h;
207 
208         PetscCall(PetscDrawStringGetSize(draw, NULL, &h));
209         PetscCall(PetscDrawStringCentered(draw, .5 * (binLeft + binRight), ymin - 1.5 * h, bcolor, labels[idx]));
210       }
211       if (color == PETSC_DRAW_ROTATE) bcolor++;
212       if (bcolor > PETSC_DRAW_BASIC_COLORS - 1) bcolor = PETSC_DRAW_BLACK + 1;
213     }
214   }
215   PetscDrawCollectiveEnd(draw);
216   if (bar->sort) PetscCall(PetscFree(perm));
217 
218   PetscCall(PetscDrawFlush(draw));
219   PetscCall(PetscDrawPause(draw));
220   PetscFunctionReturn(PETSC_SUCCESS);
221 }
222 
223 /*@
224   PetscDrawBarSave - Saves a drawn bar graph
225 
226   Collective
227 
228   Input Parameter:
229 . bar - The bar graph context
230 
231   Level: intermediate
232 
233 .seealso: `PetscDrawSave()`, `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetDraw()`, `PetscDrawSetSave()`, `PetscDrawBarSetData()`
234 @*/
235 PetscErrorCode PetscDrawBarSave(PetscDrawBar bar)
236 {
237   PetscFunctionBegin;
238   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
239   PetscCall(PetscDrawSave(bar->win));
240   PetscFunctionReturn(PETSC_SUCCESS);
241 }
242 
243 /*@
244   PetscDrawBarSetColor - Sets the color the bars will be drawn with.
245 
246   Logically Collective
247 
248   Input Parameters:
249 + bar   - The bar graph context
250 - color - one of the colors defined in petscdraw.h or `PETSC_DRAW_ROTATE` to make each bar a
251           different color
252 
253   Level: intermediate
254 
255 .seealso: `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
256 @*/
257 PetscErrorCode PetscDrawBarSetColor(PetscDrawBar bar, int color)
258 {
259   PetscFunctionBegin;
260   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
261   bar->color = color;
262   PetscFunctionReturn(PETSC_SUCCESS);
263 }
264 
265 /*@
266   PetscDrawBarSort - Sorts the values before drawing the bar chart, the bars will be in ascending order from left to right
267 
268   Logically Collective
269 
270   Input Parameters:
271 + bar       - The bar graph context
272 . sort      - `PETSC_TRUE` to sort the values
273 - tolerance - discard values less than tolerance
274 
275   Level: intermediate
276 
277 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`, `PetscDrawBarSetColor()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
278 @*/
279 PetscErrorCode PetscDrawBarSort(PetscDrawBar bar, PetscBool sort, PetscReal tolerance)
280 {
281   PetscFunctionBegin;
282   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
283   bar->sort          = sort;
284   bar->sorttolerance = tolerance;
285   PetscFunctionReturn(PETSC_SUCCESS);
286 }
287 
288 /*@
289   PetscDrawBarSetLimits - Sets the axis limits for a bar graph. If more
290   points are added after this call, the limits will be adjusted to
291   include those additional points.
292 
293   Logically Collective
294 
295   Input Parameters:
296 + bar   - The bar graph context
297 . y_min - The lower limit
298 - y_max - The upper limit
299 
300   Level: intermediate
301 
302 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetAxis()`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`
303 @*/
304 PetscErrorCode PetscDrawBarSetLimits(PetscDrawBar bar, PetscReal y_min, PetscReal y_max)
305 {
306   PetscFunctionBegin;
307   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
308   bar->ymin = y_min;
309   bar->ymax = y_max;
310   PetscFunctionReturn(PETSC_SUCCESS);
311 }
312 
313 /*@
314   PetscDrawBarGetAxis - Gets the axis context associated with a bar graph.
315   This is useful if one wants to change some axis property, such as
316   labels, color, etc. The axis context should not be destroyed by the
317   application code.
318 
319   Not Collective, axis is parallel if bar is parallel
320 
321   Input Parameter:
322 . bar - The bar graph context
323 
324   Output Parameter:
325 . axis - The axis context
326 
327   Level: intermediate
328 
329 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawAxis`, `PetscDrawAxisCreate()`
330 @*/
331 PetscErrorCode PetscDrawBarGetAxis(PetscDrawBar bar, PetscDrawAxis *axis)
332 {
333   PetscFunctionBegin;
334   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
335   PetscAssertPointer(axis, 2);
336   *axis = bar->axis;
337   PetscFunctionReturn(PETSC_SUCCESS);
338 }
339 
340 /*@
341   PetscDrawBarGetDraw - Gets the draw context associated with a bar graph.
342 
343   Not Collective, draw is parallel if bar is parallel
344 
345   Input Parameter:
346 . bar - The bar graph context
347 
348   Output Parameter:
349 . draw - The draw context
350 
351   Level: intermediate
352 
353 .seealso: `PetscDrawBar`, `PetscDraw`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
354 @*/
355 PetscErrorCode PetscDrawBarGetDraw(PetscDrawBar bar, PetscDraw *draw)
356 {
357   PetscFunctionBegin;
358   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
359   PetscAssertPointer(draw, 2);
360   *draw = bar->win;
361   PetscFunctionReturn(PETSC_SUCCESS);
362 }
363 
364 /*@
365   PetscDrawBarSetFromOptions - Sets options related to the display of the `PetscDrawBar`
366 
367   Collective
368 
369   Input Parameter:
370 . bar - the bar graph context
371 
372   Options Database Key:
373 . -bar_sort - sort the entries before drawing the bar graph
374 
375   Level: intermediate
376 
377   Note:
378   Does not set options related to the underlying `PetscDraw` or `PetscDrawAxis`
379 
380 .seealso: `PetscDrawBar`, `PetscDrawBarDestroy()`, `PetscDrawBarCreate()`, `PetscDrawBarSort()`
381 @*/
382 PetscErrorCode PetscDrawBarSetFromOptions(PetscDrawBar bar)
383 {
384   PetscBool set;
385 
386   PetscFunctionBegin;
387   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
388 
389   PetscCall(PetscOptionsHasName(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &set));
390   if (set) {
391     PetscReal tol = bar->sorttolerance;
392     PetscCall(PetscOptionsGetReal(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &tol, NULL));
393     PetscCall(PetscDrawBarSort(bar, PETSC_TRUE, tol));
394   }
395   PetscFunctionReturn(PETSC_SUCCESS);
396 }
397