1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -
4  -  Redistribution and use in source and binary forms, with or without
5  -  modification, are permitted provided that the following conditions
6  -  are met:
7  -  1. Redistributions of source code must retain the above copyright
8  -     notice, this list of conditions and the following disclaimer.
9  -  2. Redistributions in binary form must reproduce the above
10  -     copyright notice, this list of conditions and the following
11  -     disclaimer in the documentation and/or other materials
12  -     provided with the distribution.
13  -
14  -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18  -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
27 /*!
28  * \file gplot.c
29  * <pre>
30  *
31  *     Basic plotting functions
32  *          GPLOT      *gplotCreate()
33  *          void        gplotDestroy()
34  *          l_int32     gplotAddPlot()
35  *          l_int32     gplotSetScaling()
36  *          l_int32     gplotMakeOutput()
37  *          l_int32     gplotGenCommandFile()
38  *          l_int32     gplotGenDataFiles()
39  *
40  *     Quick and dirty plots
41  *          l_int32     gplotSimple1()
42  *          l_int32     gplotSimple2()
43  *          l_int32     gplotSimpleN()
44  *          l_int32     gplotSimpleXY1()
45  *          l_int32     gplotSimpleXY2()
46  *          l_int32     gplotSimpleXYN()
47  *
48  *     Serialize for I/O
49  *          GPLOT      *gplotRead()
50  *          l_int32     gplotWrite()
51  *
52  *
53  *     Utility for programmatic plotting using gnuplot 4.6 or later
54  *     Enabled:
55  *         ~ output to png (color), ps and eps (mono), latex (mono)
56  *         ~ optional title for graph
57  *         ~ optional x and y axis labels
58  *         ~ multiple plots on one frame
59  *         ~ optional title for each plot on the frame
60  *         ~ optional log scaling on either or both axes
61  *         ~ choice of 5 plot styles for each plot
62  *         ~ choice of 2 plot modes, either using one input array
63  *           (Y vs index) or two input arrays (Y vs X).  This
64  *           choice is made implicitly depending on the number of
65  *           input arrays.
66  *
67  *     Usage:
68  *         gplotCreate() initializes for plotting
69  *         gplotAddPlot() for each plot on the frame
70  *         gplotMakeOutput() to generate all output files and run gnuplot
71  *         gplotDestroy() to clean up
72  *
73  *     Example of use:
74  *         gplot = gplotCreate("tempskew", GPLOT_PNG, "Skew score vs angle",
75  *                    "angle (deg)", "score");
76  *         gplotAddPlot(gplot, natheta, nascore1, GPLOT_LINES, "plot 1");
77  *         gplotAddPlot(gplot, natheta, nascore2, GPLOT_POINTS, "plot 2");
78  *         gplotSetScaling(gplot, GPLOT_LOG_SCALE_Y);
79  *         gplotMakeOutput(gplot);
80  *         gplotDestroy(&gplot);
81  *
82  *     Note for output to GPLOT_LATEX:
83  *         This creates latex output of the plot, named <rootname>.tex.
84  *         It needs to be placed in a latex file <latexname>.tex
85  *         that precedes the plot output with, at a minimum:
86  *           \documentclass{article}
87  *           \begin{document}
88  *         and ends with
89  *           \end{document}
90  *         You can then generate a dvi file <latexname>.dvi using
91  *           latex <latexname>.tex
92  *         and a PostScript file <psname>.ps from that using
93  *           dvips -o <psname>.ps <latexname>.dvi
94  *
95  *     N.B. To generate plots, it is necessary to have gnuplot installed on
96  *          your Unix system, or wgnuplot on Windows.
97  * </pre>
98  */
99 
100 #include <string.h>
101 #include "allheaders.h"
102 
103 static const l_int32  L_BUFSIZE = 512;  /* hardcoded below in fscanf */
104 
105 const char  *gplotstylenames[] = {"with lines",
106                                   "with points",
107                                   "with impulses",
108                                   "with linespoints",
109                                   "with dots"};
110 const char  *gplotfileoutputs[] = {"",
111                                    "PNG",
112                                    "PS",
113                                    "EPS",
114                                    "LATEX"};
115 
116 
117 /*-----------------------------------------------------------------*
118  *                       Basic Plotting Functions                  *
119  *-----------------------------------------------------------------*/
120 /*!
121  * \brief   gplotCreate()
122  *
123  * \param[in]    rootname root for all output files
124  * \param[in]    outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX
125  * \param[in]    title  [optional] overall title
126  * \param[in]    xlabel [optional] x axis label
127  * \param[in]    ylabel [optional] y axis label
128  * \return  gplot, or NULL on error
129  *
130  * <pre>
131  * Notes:
132  *      (1) This initializes the plot.
133  *      (2) The 'title', 'xlabel' and 'ylabel' strings can have spaces,
134  *          double quotes and backquotes, but not single quotes.
135  * </pre>
136  */
137 GPLOT  *
gplotCreate(const char * rootname,l_int32 outformat,const char * title,const char * xlabel,const char * ylabel)138 gplotCreate(const char  *rootname,
139             l_int32      outformat,
140             const char  *title,
141             const char  *xlabel,
142             const char  *ylabel)
143 {
144 char    *newroot;
145 char     buf[L_BUFSIZE];
146 l_int32  badchar;
147 GPLOT   *gplot;
148 
149     PROCNAME("gplotCreate");
150 
151     if (!rootname)
152         return (GPLOT *)ERROR_PTR("rootname not defined", procName, NULL);
153     if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
154         outformat != GPLOT_EPS && outformat != GPLOT_LATEX)
155         return (GPLOT *)ERROR_PTR("outformat invalid", procName, NULL);
156     stringCheckForChars(rootname, "`;&|><\"?*$()", &badchar);
157     if (badchar)  /* danger of command injection */
158         return (GPLOT *)ERROR_PTR("invalid rootname", procName, NULL);
159 
160     if ((gplot = (GPLOT *)LEPT_CALLOC(1, sizeof(GPLOT))) == NULL)
161         return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL);
162     gplot->cmddata = sarrayCreate(0);
163     gplot->datanames = sarrayCreate(0);
164     gplot->plotdata = sarrayCreate(0);
165     gplot->plottitles = sarrayCreate(0);
166     gplot->plotstyles = numaCreate(0);
167 
168         /* Save title, labels, rootname, outformat, cmdname, outname */
169     newroot = genPathname(rootname, NULL);
170     gplot->rootname = newroot;
171     gplot->outformat = outformat;
172     snprintf(buf, L_BUFSIZE, "%s.cmd", rootname);
173     gplot->cmdname = stringNew(buf);
174     if (outformat == GPLOT_PNG)
175         snprintf(buf, L_BUFSIZE, "%s.png", newroot);
176     else if (outformat == GPLOT_PS)
177         snprintf(buf, L_BUFSIZE, "%s.ps", newroot);
178     else if (outformat == GPLOT_EPS)
179         snprintf(buf, L_BUFSIZE, "%s.eps", newroot);
180     else if (outformat == GPLOT_LATEX)
181         snprintf(buf, L_BUFSIZE, "%s.tex", newroot);
182     gplot->outname = stringNew(buf);
183     if (title) gplot->title = stringNew(title);
184     if (xlabel) gplot->xlabel = stringNew(xlabel);
185     if (ylabel) gplot->ylabel = stringNew(ylabel);
186 
187     return gplot;
188 }
189 
190 
191 /*!
192  * \brief    gplotDestroy()
193  *
194  * \param[in,out] pgplot to be nulled
195  */
196 void
gplotDestroy(GPLOT ** pgplot)197 gplotDestroy(GPLOT  **pgplot)
198 {
199 GPLOT  *gplot;
200 
201     PROCNAME("gplotDestroy");
202 
203     if (pgplot == NULL) {
204         L_WARNING("ptr address is null!\n", procName);
205         return;
206     }
207 
208     if ((gplot = *pgplot) == NULL)
209         return;
210 
211     LEPT_FREE(gplot->rootname);
212     LEPT_FREE(gplot->cmdname);
213     sarrayDestroy(&gplot->cmddata);
214     sarrayDestroy(&gplot->datanames);
215     sarrayDestroy(&gplot->plotdata);
216     sarrayDestroy(&gplot->plottitles);
217     numaDestroy(&gplot->plotstyles);
218     LEPT_FREE(gplot->outname);
219     if (gplot->title)
220         LEPT_FREE(gplot->title);
221     if (gplot->xlabel)
222         LEPT_FREE(gplot->xlabel);
223     if (gplot->ylabel)
224         LEPT_FREE(gplot->ylabel);
225 
226     LEPT_FREE(gplot);
227     *pgplot = NULL;
228     return;
229 }
230 
231 
232 /*!
233  * \brief   gplotAddPlot()
234  *
235  * \param[in]    gplot
236  * \param[in]    nax [optional] numa: set to null for Y_VS_I;
237  *                   required for Y_VS_X
238  * \param[in]    nay numa: required for both Y_VS_I and Y_VS_X
239  * \param[in]    plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
240  *                         GPLOT_LINESPOINTS, GPLOT_DOTS
241  * \param[in]    plottitle  [optional] title for individual plot
242  * \return  0 if OK, 1 on error
243  *
244  * <pre>
245  * Notes:
246  *      (1) There are 2 options for (x,y) values:
247  *            o  To plot an array vs a linear function of the
248  *               index, set nax = NULL.
249  *            o  To plot one array vs another, use both nax and nay.
250  *      (2) If nax is NULL, the x value corresponding to the i-th
251  *          value of nay is found from the startx and delx fields
252  *          in nay:
253  *               x = startx + i * delx
254  *          These are set with numaSetParameters().  Their default
255  *          values are startx = 0.0, delx = 1.0.
256  *      (3) If nax is defined, it must be the same size as nay, and
257  *          must have at least one number.
258  *      (4) The 'plottitle' string can have spaces, double
259  *          quotes and backquotes, but not single quotes.
260  * </pre>
261  */
262 l_int32
gplotAddPlot(GPLOT * gplot,NUMA * nax,NUMA * nay,l_int32 plotstyle,const char * plottitle)263 gplotAddPlot(GPLOT       *gplot,
264              NUMA        *nax,
265              NUMA        *nay,
266              l_int32      plotstyle,
267              const char  *plottitle)
268 {
269 char       buf[L_BUFSIZE];
270 char       emptystring[] = "";
271 char      *datastr, *title;
272 l_int32    n, i;
273 l_float32  valx, valy, startx, delx;
274 SARRAY    *sa;
275 
276     PROCNAME("gplotAddPlot");
277 
278     if (!gplot)
279         return ERROR_INT("gplot not defined", procName, 1);
280     if (!nay)
281         return ERROR_INT("nay not defined", procName, 1);
282     if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
283         return ERROR_INT("invalid plotstyle", procName, 1);
284 
285     if ((n = numaGetCount(nay)) == 0)
286         return ERROR_INT("no points to plot", procName, 1);
287     if (nax && (n != numaGetCount(nax)))
288         return ERROR_INT("nax and nay sizes differ", procName, 1);
289     if (n == 1 && plotstyle == GPLOT_LINES) {
290         L_INFO("only 1 pt; changing style to points\n", procName);
291         plotstyle = GPLOT_POINTS;
292     }
293 
294         /* Save plotstyle and plottitle */
295     numaGetParameters(nay, &startx, &delx);
296     numaAddNumber(gplot->plotstyles, plotstyle);
297     if (plottitle) {
298         title = stringNew(plottitle);
299         sarrayAddString(gplot->plottitles, title, L_INSERT);
300     } else {
301         sarrayAddString(gplot->plottitles, emptystring, L_COPY);
302     }
303 
304         /* Generate and save data filename */
305     gplot->nplots++;
306     snprintf(buf, L_BUFSIZE, "%s.data.%d", gplot->rootname, gplot->nplots);
307     sarrayAddString(gplot->datanames, buf, L_COPY);
308 
309         /* Generate data and save as a string */
310     sa = sarrayCreate(n);
311     for (i = 0; i < n; i++) {
312         if (nax)
313             numaGetFValue(nax, i, &valx);
314         else
315             valx = startx + i * delx;
316         numaGetFValue(nay, i, &valy);
317         snprintf(buf, L_BUFSIZE, "%f %f\n", valx, valy);
318         sarrayAddString(sa, buf, L_COPY);
319     }
320     datastr = sarrayToString(sa, 0);
321     sarrayAddString(gplot->plotdata, datastr, L_INSERT);
322     sarrayDestroy(&sa);
323 
324     return 0;
325 }
326 
327 
328 /*!
329  * \brief   gplotSetScaling()
330  *
331  * \param[in]    gplot
332  * \param[in]    scaling GPLOT_LINEAR_SCALE, GPLOT_LOG_SCALE_X,
333  *                       GPLOT_LOG_SCALE_Y, GPLOT_LOG_SCALE_X_Y
334  * \return  0 if OK; 1 on error
335  *
336  * <pre>
337  * Notes:
338  *      (1) By default, the x and y axis scaling is linear.
339  *      (2) Call this function to set semi-log or log-log scaling.
340  * </pre>
341  */
342 l_int32
gplotSetScaling(GPLOT * gplot,l_int32 scaling)343 gplotSetScaling(GPLOT   *gplot,
344                 l_int32  scaling)
345 {
346     PROCNAME("gplotSetScaling");
347 
348     if (!gplot)
349         return ERROR_INT("gplot not defined", procName, 1);
350     if (scaling != GPLOT_LINEAR_SCALE &&
351         scaling != GPLOT_LOG_SCALE_X &&
352         scaling != GPLOT_LOG_SCALE_Y &&
353         scaling != GPLOT_LOG_SCALE_X_Y)
354         return ERROR_INT("invalid gplot scaling", procName, 1);
355     gplot->scaling = scaling;
356     return 0;
357 }
358 
359 
360 /*!
361  * \brief   gplotMakeOutput()
362  *
363  * \param[in]    gplot
364  * \return  0 if OK; 1 on error
365  *
366  * <pre>
367  * Notes:
368  *      (1) This uses gplot and the new arrays to add a plot
369  *          to the output, by writing a new data file and appending
370  *          the appropriate plot commands to the command file.
371  *      (2) This is the only function in this file that requires the
372  *          gnuplot executable, to actually generate the plot.
373  *      (3) The command file name for unix is canonical (i.e., directory /tmp)
374  *          but the temp filename paths in the command file must be correct.
375  *      (4) The gnuplot program for windows is wgnuplot.exe.
376  * </pre>
377  */
378 l_int32
gplotMakeOutput(GPLOT * gplot)379 gplotMakeOutput(GPLOT  *gplot)
380 {
381 char     buf[L_BUFSIZE];
382 char    *cmdname;
383 l_int32  ignore;
384 
385     PROCNAME("gplotMakeOutput");
386 
387     if (!gplot)
388         return ERROR_INT("gplot not defined", procName, 1);
389 
390     gplotGenCommandFile(gplot);
391     gplotGenDataFiles(gplot);
392     cmdname = genPathname(gplot->cmdname, NULL);
393 
394 #ifndef _WIN32
395     snprintf(buf, L_BUFSIZE, "gnuplot %s", cmdname);
396 #else
397     snprintf(buf, L_BUFSIZE, "wgnuplot %s", cmdname);
398 #endif  /* _WIN32 */
399 
400 #ifndef OS_IOS /* iOS 11 does not support system() */
401     if (LeptDebugOK) {
402         ignore = system(buf);  /* gnuplot || wgnuplot */
403     } else {
404         L_INFO("running gnuplot is disabled; use setLeptDebugOK(1) to enable\n",
405                procName);
406         return 0;
407     }
408 #endif /* !OS_IOS */
409 
410     LEPT_FREE(cmdname);
411     return 0;
412 }
413 
414 
415 /*!
416  * \brief   gplotGenCommandFile()
417  *
418  * \param[in]    gplot
419  * \return  0 if OK, 1 on error
420  */
421 l_int32
gplotGenCommandFile(GPLOT * gplot)422 gplotGenCommandFile(GPLOT  *gplot)
423 {
424 char     buf[L_BUFSIZE];
425 char    *cmdstr, *plottitle, *dataname;
426 l_int32  i, plotstyle, nplots;
427 FILE    *fp;
428 
429     PROCNAME("gplotGenCommandFile");
430 
431     if (!gplot)
432         return ERROR_INT("gplot not defined", procName, 1);
433 
434         /* Remove any previous command data */
435     sarrayClear(gplot->cmddata);
436 
437         /* Generate command data instructions */
438     if (gplot->title) {   /* set title */
439         snprintf(buf, L_BUFSIZE, "set title '%s'", gplot->title);
440         sarrayAddString(gplot->cmddata, buf, L_COPY);
441     }
442     if (gplot->xlabel) {   /* set xlabel */
443         snprintf(buf, L_BUFSIZE, "set xlabel '%s'", gplot->xlabel);
444         sarrayAddString(gplot->cmddata, buf, L_COPY);
445     }
446     if (gplot->ylabel) {   /* set ylabel */
447         snprintf(buf, L_BUFSIZE, "set ylabel '%s'", gplot->ylabel);
448         sarrayAddString(gplot->cmddata, buf, L_COPY);
449     }
450 
451         /* Set terminal type and output */
452     if (gplot->outformat == GPLOT_PNG) {
453         snprintf(buf, L_BUFSIZE, "set terminal png; set output '%s'",
454                  gplot->outname);
455     } else if (gplot->outformat == GPLOT_PS) {
456         snprintf(buf, L_BUFSIZE, "set terminal postscript; set output '%s'",
457                  gplot->outname);
458     } else if (gplot->outformat == GPLOT_EPS) {
459         snprintf(buf, L_BUFSIZE,
460                  "set terminal postscript eps; set output '%s'",
461                  gplot->outname);
462     } else if (gplot->outformat == GPLOT_LATEX) {
463         snprintf(buf, L_BUFSIZE, "set terminal latex; set output '%s'",
464                  gplot->outname);
465     }
466     sarrayAddString(gplot->cmddata, buf, L_COPY);
467 
468     if (gplot->scaling == GPLOT_LOG_SCALE_X ||
469         gplot->scaling == GPLOT_LOG_SCALE_X_Y) {
470         snprintf(buf, L_BUFSIZE, "set logscale x");
471         sarrayAddString(gplot->cmddata, buf, L_COPY);
472     }
473     if (gplot->scaling == GPLOT_LOG_SCALE_Y ||
474         gplot->scaling == GPLOT_LOG_SCALE_X_Y) {
475         snprintf(buf, L_BUFSIZE, "set logscale y");
476         sarrayAddString(gplot->cmddata, buf, L_COPY);
477     }
478 
479     nplots = sarrayGetCount(gplot->datanames);
480     for (i = 0; i < nplots; i++) {
481         plottitle = sarrayGetString(gplot->plottitles, i, L_NOCOPY);
482         dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY);
483         numaGetIValue(gplot->plotstyles, i, &plotstyle);
484         if (nplots == 1) {
485             snprintf(buf, L_BUFSIZE, "plot '%s' title '%s' %s",
486                      dataname, plottitle, gplotstylenames[plotstyle]);
487         } else {
488             if (i == 0)
489                 snprintf(buf, L_BUFSIZE, "plot '%s' title '%s' %s, \\",
490                      dataname, plottitle, gplotstylenames[plotstyle]);
491             else if (i < nplots - 1)
492                 snprintf(buf, L_BUFSIZE, " '%s' title '%s' %s, \\",
493                      dataname, plottitle, gplotstylenames[plotstyle]);
494             else
495                 snprintf(buf, L_BUFSIZE, " '%s' title '%s' %s",
496                      dataname, plottitle, gplotstylenames[plotstyle]);
497         }
498         sarrayAddString(gplot->cmddata, buf, L_COPY);
499     }
500 
501         /* Write command data to file */
502     cmdstr = sarrayToString(gplot->cmddata, 1);
503     if ((fp = fopenWriteStream(gplot->cmdname, "w")) == NULL) {
504         LEPT_FREE(cmdstr);
505         return ERROR_INT("cmd stream not opened", procName, 1);
506     }
507     fwrite(cmdstr, 1, strlen(cmdstr), fp);
508     fclose(fp);
509     LEPT_FREE(cmdstr);
510     return 0;
511 }
512 
513 
514 /*!
515  * \brief   gplotGenDataFiles()
516  *
517  * \param[in]    gplot
518  * \return  0 if OK, 1 on error
519  *
520  * <pre>
521  * Notes:
522  *      (1) The pathnames in the gplot command file are actual pathnames,
523  *          which can be in temp directories.  Consequently, they must not be
524  *          rewritten by calling fopenWriteStream(), and we use fopen().
525  * </pre>
526  */
527 l_int32
gplotGenDataFiles(GPLOT * gplot)528 gplotGenDataFiles(GPLOT  *gplot)
529 {
530 char    *plotdata, *dataname;
531 l_int32  i, nplots;
532 FILE    *fp;
533 
534     PROCNAME("gplotGenDataFiles");
535 
536     if (!gplot)
537         return ERROR_INT("gplot not defined", procName, 1);
538 
539     nplots = sarrayGetCount(gplot->datanames);
540     for (i = 0; i < nplots; i++) {
541         plotdata = sarrayGetString(gplot->plotdata, i, L_NOCOPY);
542         dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY);
543         if ((fp = fopen(dataname, "w")) == NULL)
544             return ERROR_INT("datafile stream not opened", procName, 1);
545         fwrite(plotdata, 1, strlen(plotdata), fp);
546         fclose(fp);
547     }
548 
549     return 0;
550 }
551 
552 
553 /*-----------------------------------------------------------------*
554  *                       Quick and Dirty Plots                     *
555  *-----------------------------------------------------------------*/
556 /*!
557  * \brief   gplotSimple1()
558  *
559  * \param[in]    na numa; plot Y_VS_I
560  * \param[in]    outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX
561  * \param[in]    outroot root of output files
562  * \param[in]    title  [optional], can be NULL
563  * \return  0 if OK, 1 on error
564  *
565  * <pre>
566  * Notes:
567  *      (1) This gives a line plot of a numa, where the array value
568  *          is plotted vs the array index.  The plot is generated
569  *          in the specified output format; the title  is optional.
570  *      (2) When calling these simple plot functions more than once, use
571  *          different %outroot to avoid overwriting the output files.
572  * </pre>
573  */
574 l_int32
gplotSimple1(NUMA * na,l_int32 outformat,const char * outroot,const char * title)575 gplotSimple1(NUMA        *na,
576              l_int32      outformat,
577              const char  *outroot,
578              const char  *title)
579 {
580     return gplotSimpleXY1(NULL, na, GPLOT_LINES, outformat, outroot, title);
581 }
582 
583 
584 /*!
585  * \brief   gplotSimple2()
586  *
587  * \param[in]    na1 numa; plotted with Y_VS_I
588  * \param[in]    na2 ditto
589  * \param[in]    outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX
590  * \param[in]    outroot root of output files
591  * \param[in]    title  [optional]
592  * \return  0 if OK, 1 on error
593  *
594  * <pre>
595  * Notes:
596  *      (1) This gives a line plot of two numa, where the array values
597  *          are each plotted vs the array index.  The plot is generated
598  *          in the specified output format; the title  is optional.
599  *      (2) When calling these simple plot functions more than once, use
600  *          different %outroot to avoid overwriting the output files.
601  * </pre>
602  */
603 l_int32
gplotSimple2(NUMA * na1,NUMA * na2,l_int32 outformat,const char * outroot,const char * title)604 gplotSimple2(NUMA        *na1,
605              NUMA        *na2,
606              l_int32      outformat,
607              const char  *outroot,
608              const char  *title)
609 {
610     return gplotSimpleXY2(NULL, na1, na2, GPLOT_LINES,
611                           outformat, outroot, title);
612 }
613 
614 
615 /*!
616  * \brief   gplotSimpleN()
617  *
618  * \param[in]    naa numaa; we plotted with Y_VS_I for each numa
619  * \param[in]    outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX
620  * \param[in]    outroot root of output files
621  * \param[in]    title [optional]
622  * \return  0 if OK, 1 on error
623  *
624  * <pre>
625  * Notes:
626  *      (1) This gives a line plot of all numas in a numaa (array of numa),
627  *          where the array values are each plotted vs the array index.
628  *          The plot is generated in the specified output format;
629  *          the title  is optional.
630  *      (2) When calling these simple plot functions more than once, use
631  *          different %outroot to avoid overwriting the output files.
632  * </pre>
633  */
634 l_int32
gplotSimpleN(NUMAA * naa,l_int32 outformat,const char * outroot,const char * title)635 gplotSimpleN(NUMAA       *naa,
636              l_int32      outformat,
637              const char  *outroot,
638              const char  *title)
639 {
640     return gplotSimpleXYN(NULL, naa, GPLOT_LINES, outformat, outroot, title);
641 }
642 
643 
644 /*!
645  * \brief   gplotSimpleXY1()
646  *
647  * \param[in]    nax [optional]
648  * \param[in]    nay
649  * \param[in]    plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
650  *                         GPLOT_LINESPOINTS, GPLOT_DOTS
651  * \param[in]    outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX
652  * \param[in]    outroot root of output files
653  * \param[in]    title  [optional], can be NULL
654  * \return  0 if OK, 1 on error
655  *
656  * <pre>
657  * Notes:
658  *      (1) This gives a plot of a %nay vs %nax, generated in
659  *          the specified output format.  The title is optional.
660  *      (2) Use 0 for default plotstyle (lines).
661  *      (3) %nax is optional.  If NULL, %nay is plotted against
662  *          the array index.
663  *      (4) When calling these simple plot functions more than once, use
664  *          different %outroot to avoid overwriting the output files.
665  * </pre>
666  */
667 l_int32
gplotSimpleXY1(NUMA * nax,NUMA * nay,l_int32 plotstyle,l_int32 outformat,const char * outroot,const char * title)668 gplotSimpleXY1(NUMA        *nax,
669                NUMA        *nay,
670                l_int32      plotstyle,
671                l_int32      outformat,
672                const char  *outroot,
673                const char  *title)
674 {
675 GPLOT  *gplot;
676 
677     PROCNAME("gplotSimpleXY1");
678 
679     if (!nay)
680         return ERROR_INT("nay not defined", procName, 1);
681     if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
682         return ERROR_INT("invalid plotstyle", procName, 1);
683     if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
684         outformat != GPLOT_EPS && outformat != GPLOT_LATEX)
685         return ERROR_INT("invalid outformat", procName, 1);
686     if (!outroot)
687         return ERROR_INT("outroot not specified", procName, 1);
688 
689     if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0)
690         return ERROR_INT("gplot not made", procName, 1);
691     gplotAddPlot(gplot, nax, nay, plotstyle, NULL);
692     gplotMakeOutput(gplot);
693     gplotDestroy(&gplot);
694     return 0;
695 }
696 
697 
698 /*!
699  * \brief   gplotSimpleXY2()
700  *
701  * \param[in]    nax <optional; can be NULL
702  * \param[in]    nay1
703  * \param[in]    nay2
704  * \param[in]    plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
705  *                         GPLOT_LINESPOINTS, GPLOT_DOTS
706  * \param[in]    outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX
707  * \param[in]    outroot root of output files
708  * \param[in]    title  [optional]
709  * \return  0 if OK, 1 on error
710  *
711  * <pre>
712  * Notes:
713  *      (1) This gives plots of %nay1 and %nay2 against nax, generated
714  *          in the specified output format.  The title is optional.
715  *      (2) Use 0 for default plotstyle (lines).
716  *      (3) %nax is optional.  If NULL, %nay1 and %nay2 are plotted
717  *          against the array index.
718  *      (4) When calling these simple plot functions more than once, use
719  *          different %outroot to avoid overwriting the output files.
720  * </pre>
721  */
722 l_int32
gplotSimpleXY2(NUMA * nax,NUMA * nay1,NUMA * nay2,l_int32 plotstyle,l_int32 outformat,const char * outroot,const char * title)723 gplotSimpleXY2(NUMA        *nax,
724                NUMA        *nay1,
725                NUMA        *nay2,
726                l_int32      plotstyle,
727                l_int32      outformat,
728                const char  *outroot,
729                const char  *title)
730 {
731 GPLOT  *gplot;
732 
733     PROCNAME("gplotSimpleXY2");
734 
735     if (!nay1 || !nay2)
736         return ERROR_INT("nay1 and nay2 not both defined", procName, 1);
737     if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
738         return ERROR_INT("invalid plotstyle", procName, 1);
739     if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
740         outformat != GPLOT_EPS && outformat != GPLOT_LATEX)
741         return ERROR_INT("invalid outformat", procName, 1);
742     if (!outroot)
743         return ERROR_INT("outroot not specified", procName, 1);
744 
745     if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0)
746         return ERROR_INT("gplot not made", procName, 1);
747     gplotAddPlot(gplot, nax, nay1, plotstyle, NULL);
748     gplotAddPlot(gplot, nax, nay2, plotstyle, NULL);
749     gplotMakeOutput(gplot);
750     gplotDestroy(&gplot);
751     return 0;
752 }
753 
754 
755 /*!
756  * \brief   gplotSimpleXYN()
757  *
758  * \param[in]    nax [optional]; can be NULL
759  * \param[in]    naay numaa of arrays to plot against %nax
760  * \param[in]    plotstyle GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
761  *                         GPLOT_LINESPOINTS, GPLOT_DOTS
762  * \param[in]    outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX
763  * \param[in]    outroot root of output files
764  * \param[in]    title [optional]
765  * \return  0 if OK, 1 on error
766  *
767  * <pre>
768  * Notes:
769  *      (1) This gives plots of each Numa in %naa against nax,
770  *          generated in the specified output format.  The title is optional.
771  *      (2) Use 0 for default plotstyle (lines).
772  *      (3) %nax is optional.  If NULL, each Numa array is plotted against
773  *          the array index.
774  *      (4) When calling these simple plot functions more than once, use
775  *          different %outroot to avoid overwriting the output files.
776  * </pre>
777  */
778 l_int32
gplotSimpleXYN(NUMA * nax,NUMAA * naay,l_int32 plotstyle,l_int32 outformat,const char * outroot,const char * title)779 gplotSimpleXYN(NUMA        *nax,
780                NUMAA       *naay,
781                l_int32      plotstyle,
782                l_int32      outformat,
783                const char  *outroot,
784                const char  *title)
785 {
786 l_int32  i, n;
787 GPLOT   *gplot;
788 NUMA    *nay;
789 
790     PROCNAME("gplotSimpleXYN");
791 
792     if (!naay)
793         return ERROR_INT("naay not defined", procName, 1);
794     if ((n = numaaGetCount(naay)) == 0)
795         return ERROR_INT("no numa in array", procName, 1);
796     if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
797         return ERROR_INT("invalid plotstyle", procName, 1);
798     if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
799         outformat != GPLOT_EPS && outformat != GPLOT_LATEX)
800         return ERROR_INT("invalid outformat", procName, 1);
801     if (!outroot)
802         return ERROR_INT("outroot not specified", procName, 1);
803 
804     if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0)
805         return ERROR_INT("gplot not made", procName, 1);
806     for (i = 0; i < n; i++) {
807         nay = numaaGetNuma(naay, i, L_CLONE);
808         gplotAddPlot(gplot, nax, nay, plotstyle, NULL);
809         numaDestroy(&nay);
810     }
811     gplotMakeOutput(gplot);
812     gplotDestroy(&gplot);
813     return 0;
814 }
815 
816 
817 /*-----------------------------------------------------------------*
818  *                           Serialize for I/O                     *
819  *-----------------------------------------------------------------*/
820 /*!
821  * \brief   gplotRead()
822  *
823  * \param[in]    filename
824  * \return  gplot, or NULL on error
825  */
826 GPLOT *
gplotRead(const char * filename)827 gplotRead(const char  *filename)
828 {
829 char     buf[L_BUFSIZE];
830 char    *rootname, *title, *xlabel, *ylabel, *ignores;
831 l_int32  outformat, ret, version, ignore;
832 FILE    *fp;
833 GPLOT   *gplot;
834 
835     PROCNAME("gplotRead");
836 
837     if (!filename)
838         return (GPLOT *)ERROR_PTR("filename not defined", procName, NULL);
839 
840     if ((fp = fopenReadStream(filename)) == NULL)
841         return (GPLOT *)ERROR_PTR("stream not opened", procName, NULL);
842 
843     ret = fscanf(fp, "Gplot Version %d\n", &version);
844     if (ret != 1) {
845         fclose(fp);
846         return (GPLOT *)ERROR_PTR("not a gplot file", procName, NULL);
847     }
848     if (version != GPLOT_VERSION_NUMBER) {
849         fclose(fp);
850         return (GPLOT *)ERROR_PTR("invalid gplot version", procName, NULL);
851     }
852 
853     ignore = fscanf(fp, "Rootname: %511s\n", buf);  /* L_BUFSIZE - 1 */
854     rootname = stringNew(buf);
855     ignore = fscanf(fp, "Output format: %d\n", &outformat);
856     ignores = fgets(buf, L_BUFSIZE, fp);   /* Title: ... */
857     title = stringNew(buf + 7);
858     title[strlen(title) - 1] = '\0';
859     ignores = fgets(buf, L_BUFSIZE, fp);   /* X axis label: ... */
860     xlabel = stringNew(buf + 14);
861     xlabel[strlen(xlabel) - 1] = '\0';
862     ignores = fgets(buf, L_BUFSIZE, fp);   /* Y axis label: ... */
863     ylabel = stringNew(buf + 14);
864     ylabel[strlen(ylabel) - 1] = '\0';
865 
866     gplot = gplotCreate(rootname, outformat, title, xlabel, ylabel);
867     LEPT_FREE(rootname);
868     LEPT_FREE(title);
869     LEPT_FREE(xlabel);
870     LEPT_FREE(ylabel);
871     if (!gplot) {
872         fclose(fp);
873         return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL);
874     }
875     sarrayDestroy(&gplot->cmddata);
876     sarrayDestroy(&gplot->datanames);
877     sarrayDestroy(&gplot->plotdata);
878     sarrayDestroy(&gplot->plottitles);
879     numaDestroy(&gplot->plotstyles);
880 
881     ignore = fscanf(fp, "Commandfile name: %511s\n", buf);  /* L_BUFSIZE - 1 */
882     stringReplace(&gplot->cmdname, buf);
883     ignore = fscanf(fp, "\nCommandfile data:");
884     gplot->cmddata = sarrayReadStream(fp);
885     ignore = fscanf(fp, "\nDatafile names:");
886     gplot->datanames = sarrayReadStream(fp);
887     ignore = fscanf(fp, "\nPlot data:");
888     gplot->plotdata = sarrayReadStream(fp);
889     ignore = fscanf(fp, "\nPlot titles:");
890     gplot->plottitles = sarrayReadStream(fp);
891     ignore = fscanf(fp, "\nPlot styles:");
892     gplot->plotstyles = numaReadStream(fp);
893 
894     ignore = fscanf(fp, "Number of plots: %d\n", &gplot->nplots);
895     ignore = fscanf(fp, "Output file name: %511s\n", buf);
896     stringReplace(&gplot->outname, buf);
897     ignore = fscanf(fp, "Axis scaling: %d\n", &gplot->scaling);
898 
899     fclose(fp);
900     return gplot;
901 }
902 
903 
904 /*!
905  * \brief   gplotWrite()
906  *
907  * \param[in]    filename
908  * \param[in]    gplot
909  * \return  0 if OK; 1 on error
910  */
911 l_int32
gplotWrite(const char * filename,GPLOT * gplot)912 gplotWrite(const char  *filename,
913            GPLOT       *gplot)
914 {
915 FILE  *fp;
916 
917     PROCNAME("gplotWrite");
918 
919     if (!filename)
920         return ERROR_INT("filename not defined", procName, 1);
921     if (!gplot)
922         return ERROR_INT("gplot not defined", procName, 1);
923 
924     if ((fp = fopenWriteStream(filename, "wb")) == NULL)
925         return ERROR_INT("stream not opened", procName, 1);
926 
927     fprintf(fp, "Gplot Version %d\n", GPLOT_VERSION_NUMBER);
928     fprintf(fp, "Rootname: %s\n", gplot->rootname);
929     fprintf(fp, "Output format: %d\n", gplot->outformat);
930     fprintf(fp, "Title: %s\n", gplot->title);
931     fprintf(fp, "X axis label: %s\n", gplot->xlabel);
932     fprintf(fp, "Y axis label: %s\n", gplot->ylabel);
933 
934     fprintf(fp, "Commandfile name: %s\n", gplot->cmdname);
935     fprintf(fp, "\nCommandfile data:");
936     sarrayWriteStream(fp, gplot->cmddata);
937     fprintf(fp, "\nDatafile names:");
938     sarrayWriteStream(fp, gplot->datanames);
939     fprintf(fp, "\nPlot data:");
940     sarrayWriteStream(fp, gplot->plotdata);
941     fprintf(fp, "\nPlot titles:");
942     sarrayWriteStream(fp, gplot->plottitles);
943     fprintf(fp, "\nPlot styles:");
944     numaWriteStream(fp, gplot->plotstyles);
945 
946     fprintf(fp, "Number of plots: %d\n", gplot->nplots);
947     fprintf(fp, "Output file name: %s\n", gplot->outname);
948     fprintf(fp, "Axis scaling: %d\n", gplot->scaling);
949 
950     fclose(fp);
951     return 0;
952 }
953