xref: /petsc/src/sys/classes/draw/utils/bars.c (revision 3f02e49b19195914bf17f317a25cb39636853415)
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   }
87   PetscCall(PetscArraycpy(bar->values, data, bins));
88   PetscCall(PetscCIntCast(bins, &bar->numBins));
89   if (labels) PetscCall(PetscStrArrayallocpy(labels, &bar->labels));
90   PetscFunctionReturn(PETSC_SUCCESS);
91 }
92 
93 /*@
94   PetscDrawBarDestroy - Frees all space taken up by bar graph data structure.
95 
96   Collective
97 
98   Input Parameter:
99 . bar - The bar graph context
100 
101   Level: intermediate
102 
103 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`
104 @*/
105 PetscErrorCode PetscDrawBarDestroy(PetscDrawBar *bar)
106 {
107   PetscFunctionBegin;
108   if (!*bar) PetscFunctionReturn(PETSC_SUCCESS);
109   PetscValidHeaderSpecific(*bar, PETSC_DRAWBAR_CLASSID, 1);
110   if (--((PetscObject)*bar)->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
111 
112   PetscCall(PetscFree((*bar)->values));
113   PetscCall(PetscStrArrayDestroy(&(*bar)->labels));
114   PetscCall(PetscDrawAxisDestroy(&(*bar)->axis));
115   PetscCall(PetscDrawDestroy(&(*bar)->win));
116   PetscCall(PetscHeaderDestroy(bar));
117   PetscFunctionReturn(PETSC_SUCCESS);
118 }
119 
120 /*@
121   PetscDrawBarDraw - Redraws a bar graph.
122 
123   Collective
124 
125   Input Parameter:
126 . bar - The bar graph context
127 
128   Level: intermediate
129 
130 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`
131 @*/
132 PetscErrorCode PetscDrawBarDraw(PetscDrawBar bar)
133 {
134   PetscDraw   draw;
135   PetscBool   isnull;
136   PetscReal   xmin, xmax, ymin, ymax, *values, binLeft, binRight;
137   PetscInt    numValues, i, idx, *perm, nplot;
138   PetscMPIInt rank;
139   char      **labels;
140   int         bcolor, color;
141 
142   PetscFunctionBegin;
143   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
144   PetscCall(PetscDrawIsNull(bar->win, &isnull));
145   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
146   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)bar), &rank));
147 
148   if (bar->numBins < 1) PetscFunctionReturn(PETSC_SUCCESS);
149 
150   color = bar->color;
151   if (color == PETSC_DRAW_ROTATE) bcolor = PETSC_DRAW_BLACK + 1;
152   else bcolor = color;
153 
154   numValues = bar->numBins;
155   values    = bar->values;
156   if (bar->ymin == bar->ymax) {
157     /* user has not set bounds on bars so set them based on the data */
158     ymin = PETSC_MAX_REAL;
159     ymax = PETSC_MIN_REAL;
160     for (i = 0; i < numValues; i++) {
161       ymin = PetscMin(ymin, values[i]);
162       ymax = PetscMax(ymax, values[i]);
163     }
164   } else {
165     ymin = bar->ymin;
166     ymax = bar->ymax;
167   }
168   nplot  = numValues; /* number of points to actually plot; if some are lower than requested tolerance */
169   xmin   = 0.0;
170   xmax   = (PetscReal)nplot;
171   labels = bar->labels;
172 
173   if (bar->sort) {
174     PetscCall(PetscMalloc1(numValues, &perm));
175     for (i = 0; i < numValues; i++) perm[i] = i;
176     PetscCall(PetscSortRealWithPermutation(numValues, values, perm));
177     if (bar->sorttolerance) {
178       for (i = 0; i < numValues; i++) {
179         if (values[perm[numValues - i - 1]] < bar->sorttolerance) {
180           nplot = i;
181           break;
182         }
183       }
184     }
185   }
186 
187   draw = bar->win;
188   PetscCall(PetscDrawCheckResizedWindow(draw));
189   PetscCall(PetscDrawClear(draw));
190 
191   PetscCall(PetscDrawAxisSetLimits(bar->axis, xmin, xmax, ymin, ymax));
192   PetscCall(PetscDrawAxisDraw(bar->axis));
193 
194   PetscDrawCollectiveBegin(draw);
195   if (rank == 0) { /* Draw bins */
196     for (i = 0; i < nplot; i++) {
197       idx      = (bar->sort ? perm[numValues - i - 1] : i);
198       binLeft  = xmin + (PetscReal)i;
199       binRight = xmin + (PetscReal)i + 1;
200       PetscCall(PetscDrawRectangle(draw, binLeft, ymin, binRight, values[idx], bcolor, bcolor, bcolor, bcolor));
201       PetscCall(PetscDrawLine(draw, binLeft, ymin, binLeft, values[idx], PETSC_DRAW_BLACK));
202       PetscCall(PetscDrawLine(draw, binRight, ymin, binRight, values[idx], PETSC_DRAW_BLACK));
203       PetscCall(PetscDrawLine(draw, binLeft, values[idx], binRight, values[idx], PETSC_DRAW_BLACK));
204       if (labels) {
205         PetscReal h;
206 
207         PetscCall(PetscDrawStringGetSize(draw, NULL, &h));
208         PetscCall(PetscDrawStringCentered(draw, .5 * (binLeft + binRight), ymin - 1.5 * h, bcolor, labels[idx]));
209       }
210       if (color == PETSC_DRAW_ROTATE) bcolor++;
211       if (bcolor > PETSC_DRAW_BASIC_COLORS - 1) bcolor = PETSC_DRAW_BLACK + 1;
212     }
213   }
214   PetscDrawCollectiveEnd(draw);
215   if (bar->sort) PetscCall(PetscFree(perm));
216 
217   PetscCall(PetscDrawFlush(draw));
218   PetscCall(PetscDrawPause(draw));
219   PetscFunctionReturn(PETSC_SUCCESS);
220 }
221 
222 /*@
223   PetscDrawBarSave - Saves a drawn bar graph
224 
225   Collective
226 
227   Input Parameter:
228 . bar - The bar graph context
229 
230   Level: intermediate
231 
232 .seealso: `PetscDrawSave()`, `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetDraw()`, `PetscDrawSetSave()`, `PetscDrawBarSetData()`
233 @*/
234 PetscErrorCode PetscDrawBarSave(PetscDrawBar bar)
235 {
236   PetscFunctionBegin;
237   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
238   PetscCall(PetscDrawSave(bar->win));
239   PetscFunctionReturn(PETSC_SUCCESS);
240 }
241 
242 /*@
243   PetscDrawBarSetColor - Sets the color the bars will be drawn with.
244 
245   Logically Collective
246 
247   Input Parameters:
248 + bar   - The bar graph context
249 - color - one of the colors defined in petscdraw.h or `PETSC_DRAW_ROTATE` to make each bar a
250           different color
251 
252   Level: intermediate
253 
254 .seealso: `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
255 @*/
256 PetscErrorCode PetscDrawBarSetColor(PetscDrawBar bar, int color)
257 {
258   PetscFunctionBegin;
259   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
260   bar->color = color;
261   PetscFunctionReturn(PETSC_SUCCESS);
262 }
263 
264 /*@
265   PetscDrawBarSort - Sorts the values before drawing the bar chart, the bars will be in ascending order from left to right
266 
267   Logically Collective
268 
269   Input Parameters:
270 + bar       - The bar graph context
271 . sort      - `PETSC_TRUE` to sort the values
272 - tolerance - discard values less than tolerance
273 
274   Level: intermediate
275 
276 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`, `PetscDrawBarSetColor()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
277 @*/
278 PetscErrorCode PetscDrawBarSort(PetscDrawBar bar, PetscBool sort, PetscReal tolerance)
279 {
280   PetscFunctionBegin;
281   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
282   bar->sort          = sort;
283   bar->sorttolerance = tolerance;
284   PetscFunctionReturn(PETSC_SUCCESS);
285 }
286 
287 /*@
288   PetscDrawBarSetLimits - Sets the axis limits for a bar graph. If more
289   points are added after this call, the limits will be adjusted to
290   include those additional points.
291 
292   Logically Collective
293 
294   Input Parameters:
295 + bar   - The bar graph context
296 . y_min - The lower limit
297 - y_max - The upper limit
298 
299   Level: intermediate
300 
301 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetAxis()`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`
302 @*/
303 PetscErrorCode PetscDrawBarSetLimits(PetscDrawBar bar, PetscReal y_min, PetscReal y_max)
304 {
305   PetscFunctionBegin;
306   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
307   bar->ymin = y_min;
308   bar->ymax = y_max;
309   PetscFunctionReturn(PETSC_SUCCESS);
310 }
311 
312 /*@
313   PetscDrawBarGetAxis - Gets the axis context associated with a bar graph.
314   This is useful if one wants to change some axis property, such as
315   labels, color, etc. The axis context should not be destroyed by the
316   application code.
317 
318   Not Collective, axis is parallel if bar is parallel
319 
320   Input Parameter:
321 . bar - The bar graph context
322 
323   Output Parameter:
324 . axis - The axis context
325 
326   Level: intermediate
327 
328 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawAxis`, `PetscDrawAxisCreate()`
329 @*/
330 PetscErrorCode PetscDrawBarGetAxis(PetscDrawBar bar, PetscDrawAxis *axis)
331 {
332   PetscFunctionBegin;
333   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
334   PetscAssertPointer(axis, 2);
335   *axis = bar->axis;
336   PetscFunctionReturn(PETSC_SUCCESS);
337 }
338 
339 /*@
340   PetscDrawBarGetDraw - Gets the draw context associated with a bar graph.
341 
342   Not Collective, draw is parallel if bar is parallel
343 
344   Input Parameter:
345 . bar - The bar graph context
346 
347   Output Parameter:
348 . draw - The draw context
349 
350   Level: intermediate
351 
352 .seealso: `PetscDrawBar`, `PetscDraw`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
353 @*/
354 PetscErrorCode PetscDrawBarGetDraw(PetscDrawBar bar, PetscDraw *draw)
355 {
356   PetscFunctionBegin;
357   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
358   PetscAssertPointer(draw, 2);
359   *draw = bar->win;
360   PetscFunctionReturn(PETSC_SUCCESS);
361 }
362 
363 /*@
364   PetscDrawBarSetFromOptions - Sets options related to the display of the `PetscDrawBar`
365 
366   Collective
367 
368   Input Parameter:
369 . bar - the bar graph context
370 
371   Options Database Key:
372 . -bar_sort - sort the entries before drawing the bar graph
373 
374   Level: intermediate
375 
376   Note:
377   Does not set options related to the underlying `PetscDraw` or `PetscDrawAxis`
378 
379 .seealso: `PetscDrawBar`, `PetscDrawBarDestroy()`, `PetscDrawBarCreate()`, `PetscDrawBarSort()`
380 @*/
381 PetscErrorCode PetscDrawBarSetFromOptions(PetscDrawBar bar)
382 {
383   PetscBool set;
384 
385   PetscFunctionBegin;
386   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
387 
388   PetscCall(PetscOptionsHasName(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &set));
389   if (set) {
390     PetscReal tol = bar->sorttolerance;
391     PetscCall(PetscOptionsGetReal(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &tol, NULL));
392     PetscCall(PetscDrawBarSort(bar, PETSC_TRUE, tol));
393   }
394   PetscFunctionReturn(PETSC_SUCCESS);
395 }
396