Coverage Report

Created: 2025-06-13 07:02

/src/leptonica/src/gplot.c
Line
Count
Source (jump to first uncovered line)
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
 *          PIX        *gplotMakeOutputPix()
37
 *          l_int32     gplotMakeOutput()
38
 *          l_int32     gplotGenCommandFile()
39
 *          l_int32     gplotGenDataFiles()
40
 *
41
 *     Quick, one-line plots
42
 *          l_int32     gplotSimple1()
43
 *          l_int32     gplotSimple2()
44
 *          l_int32     gplotSimpleN()
45
 *          PIX        *gplotSimplePix1()
46
 *          PIX        *gplotSimplePix2()
47
 *          PIX        *gplotSimplePixN()
48
 *          GPLOT      *gplotSimpleXY1()
49
 *          GPLOT      *gplotSimpleXY2()
50
 *          GPLOT      *gplotSimpleXYN()
51
 *          PIX        *gplotGeneralPix1()
52
 *          PIX        *gplotGeneralPix2()
53
 *          PIX        *gplotGeneralPixN()
54
 *
55
 *     Serialize for I/O
56
 *          GPLOT      *gplotRead()
57
 *          l_int32     gplotWrite()
58
 *
59
 *
60
 *     Utility for programmatic plotting using gnuplot 4.6 or later
61
 *     Enabled:
62
 *         ~ output to png (color), ps and eps (mono), latex (mono)
63
 *         ~ optional title for plot
64
 *         ~ optional x and y axis labels
65
 *         ~ multiple plots on one frame
66
 *         ~ optional label for each plot on the frame
67
 *         ~ optional log scaling on either or both axes
68
 *         ~ choice of 5 plot styles for each array of input data
69
 *         ~ choice of 2 plot modes, either using one input array
70
 *           (Y vs index) or two input arrays (Y vs X).  For functions
71
 *           that take two arrays, the first mode (Y vs index) is
72
 *           employed if the first array is NULL.
73
 *
74
 *     General usage:
75
 *         gplotCreate() initializes for plotting
76
 *         gplotAddPlot() for each plot on the frame
77
 *         gplotMakeOutput() to generate all output files and run gnuplot
78
 *         gplotDestroy() to clean up
79
 *
80
 *     Example of use:
81
 *         gplot = gplotCreate("tempskew", GPLOT_PNG, "Skew score vs angle",
82
 *                    "angle (deg)", "score");
83
 *         gplotAddPlot(gplot, natheta, nascore1, GPLOT_LINES, "plot 1");
84
 *         gplotAddPlot(gplot, natheta, nascore2, GPLOT_POINTS, "plot 2");
85
 *         gplotSetScaling(gplot, GPLOT_LOG_SCALE_Y);
86
 *         gplotMakeOutput(gplot);
87
 *         gplotDestroy(&gplot);
88
 *
89
 *     Example usage of one-line plot generators:
90
 *
91
 *         -- Simple plots --
92
 *         Specify the root of output files, the output format,
93
 *         and the title (optional), but not the x and y coordinate labels
94
 *         or the plot labels.  The plotstyle defaults to GPLOT_LINES.
95
 *            gplotSimple2(na1, na2, GPLOT_PNG, "/tmp/lept/histo/gray",
96
 *                         "gray histogram");
97
 *         Multiple plots can be generated using gplotSimpleN().
98
 *
99
 *         -- Simple plots with more options --
100
 *         Specify the root of output files, the plotstyle, the output format,
101
 *         and optionally the title, but not the x and y coordinate labels
102
 *         or the plot labels.
103
 *            gplotSimpleXY1(na1, na2, GPLOT_LINES, GPLOT_PNG,
104
 *                           "/tmp/lept/histo/gray", "gray histogram");
105
 *         Multiple plots can be generated using gplotSimpleXYN().
106
 *
107
 *         -- Simple plots returning a pix --
108
 *         Specify only the title (optional).  The plotstyle defaults
109
 *         GPLOT_LINES and the output format is GPLOT_PNG..
110
 *         You can't specify the x and y coordinate labels or the plot label.
111
 *         The rootname of the generated files is determined internally.
112
 *            Pix *pix = gplotSimplePix2(na1, na2, "gray histogram");
113
 *         Multiple plots can be generated using gplotSimplePixN().
114
 *
115
 *         -- General plots returning a pix --
116
 *         Specify the root of the output files, the plotstyle, and optionally
117
 *         the title and axis labels.  This does not allow the individual
118
 *         plots to have plot labels, or to use different plotstyles
119
 *         for each plot.
120
 *            Pix *pix = gplotGeneralPix2(na1, na2, "/tmp/lept/histo/gray",
121
 *                                   GPLOT_LINES, "gray histogram",
122
 *                                   "pix value", "num pixels");
123
 *         Multiple plots can be generated using gplotGeneralPixN().
124
 *
125
 *     Note for output to GPLOT_LATEX:
126
 *         This creates latex output of the plot, named <rootname>.tex.
127
 *         It needs to be placed in a latex file <latexname>.tex
128
 *         that precedes the plot output with, at a minimum:
129
 *           \documentclass{article}
130
 *           \begin{document}
131
 *         and ends with
132
 *           \end{document}
133
 *         You can then generate a dvi file <latexname>.dvi using
134
 *           latex <latexname>.tex
135
 *         a PostScript file <psname>.ps from that using
136
 *           dvips -o <psname>.ps <latexname>.dvi
137
 *         and pdf file <psname>.pdf from that using Ghostscript's ps2pdf:
138
 *           ps2pdf <psname>.ps <pdfname>.pdf
139
 *
140
 *     N.B. To generate plots:
141
 *          (1) It is necessary to have gnuplot installed on your Unix system,
142
 *              or wgnuplot on Windows.
143
 *          (2) You must enable debug operations:
144
 *                setLeptDebugOK(1);
145
 * </pre>
146
 */
147
148
#ifdef HAVE_CONFIG_H
149
#include <config_auto.h>
150
#endif  /* HAVE_CONFIG_H */
151
152
#include <string.h>
153
#include "allheaders.h"
154
155
0
#define Bufsize 512  /* hardcoded below in fscanf */
156
157
const char  *gplotstylenames[] = {"with lines",
158
                                  "with points",
159
                                  "with impulses",
160
                                  "with linespoints",
161
                                  "with dots"};
162
const char  *gplotfileoutputs[] = {"",
163
                                   "PNG",
164
                                   "PS",
165
                                   "EPS",
166
                                   "LATEX",
167
                                   "PNM"};
168
169
170
/*-----------------------------------------------------------------*
171
 *                       Basic Plotting Functions                  *
172
 *-----------------------------------------------------------------*/
173
/*!
174
 * \brief   gplotCreate()
175
 *
176
 * \param[in]    rootname    root for all output files
177
 * \param[in]    outformat   GPLOT_PNG, GPLOT_PS, GPLOT_EPS,
178
 *                           GPLOT_LATEX, GPLOT_PNM
179
 * \param[in]    title       [optional] overall title
180
 * \param[in]    xlabel      [optional] x axis label
181
 * \param[in]    ylabel      [optional] y axis label
182
 * \return  gplot, or NULL on error
183
 *
184
 * <pre>
185
 * Notes:
186
 *      (1) This initializes the plot.
187
 *      (2) The 'title', 'xlabel' and 'ylabel' strings can have spaces,
188
 *          double quotes and backquotes, but not single quotes.
189
 * </pre>
190
 */
191
GPLOT  *
192
gplotCreate(const char  *rootname,
193
            l_int32      outformat,
194
            const char  *title,
195
            const char  *xlabel,
196
            const char  *ylabel)
197
0
{
198
0
char    *newroot;
199
0
char     buf[Bufsize];
200
0
l_int32  badchar;
201
0
GPLOT   *gplot;
202
203
0
    if (!rootname)
204
0
        return (GPLOT *)ERROR_PTR("rootname not defined", __func__, NULL);
205
0
    if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
206
0
        outformat != GPLOT_EPS && outformat != GPLOT_LATEX &&
207
0
        outformat != GPLOT_PNM)
208
0
        return (GPLOT *)ERROR_PTR("outformat invalid", __func__, NULL);
209
0
    stringCheckForChars(rootname, "`;&|><\"?*$()", &badchar);
210
0
    if (badchar)  /* danger of command injection */
211
0
        return (GPLOT *)ERROR_PTR("invalid rootname", __func__, NULL);
212
213
0
#if !defined(HAVE_LIBPNG)
214
0
    if (outformat == GPLOT_PNG) {
215
0
        L_WARNING("png library missing; output pnm format\n", __func__);
216
0
        outformat = GPLOT_PNM;
217
0
    }
218
0
#endif
219
220
0
    gplot = (GPLOT *)LEPT_CALLOC(1, sizeof(GPLOT));
221
0
    gplot->cmddata = sarrayCreate(0);
222
0
    gplot->datanames = sarrayCreate(0);
223
0
    gplot->plotdata = sarrayCreate(0);
224
0
    gplot->plotlabels = sarrayCreate(0);
225
0
    gplot->plotstyles = numaCreate(0);
226
227
        /* Save title, labels, rootname, outformat, cmdname, outname */
228
0
    newroot = genPathname(rootname, NULL);
229
0
    gplot->rootname = newroot;
230
0
    gplot->outformat = outformat;
231
0
    snprintf(buf, Bufsize, "%s.cmd", rootname);
232
0
    gplot->cmdname = stringNew(buf);
233
0
    if (outformat == GPLOT_PNG)
234
0
        snprintf(buf, Bufsize, "%s.png", newroot);
235
0
    else if (outformat == GPLOT_PS)
236
0
        snprintf(buf, Bufsize, "%s.ps", newroot);
237
0
    else if (outformat == GPLOT_EPS)
238
0
        snprintf(buf, Bufsize, "%s.eps", newroot);
239
0
    else if (outformat == GPLOT_LATEX)
240
0
        snprintf(buf, Bufsize, "%s.tex", newroot);
241
0
    else if (outformat == GPLOT_PNM)
242
0
        snprintf(buf, Bufsize, "%s.pnm", newroot);
243
0
    gplot->outname = stringNew(buf);
244
0
    if (title) gplot->title = stringNew(title);
245
0
    if (xlabel) gplot->xlabel = stringNew(xlabel);
246
0
    if (ylabel) gplot->ylabel = stringNew(ylabel);
247
248
0
    return gplot;
249
0
}
250
251
252
/*!
253
 * \brief    gplotDestroy()
254
 *
255
 * \param[in,out]   pgplot    will be set to null before returning
256
 */
257
void
258
gplotDestroy(GPLOT  **pgplot)
259
0
{
260
0
GPLOT  *gplot;
261
262
0
    if (pgplot == NULL) {
263
0
        L_WARNING("ptr address is null!\n", __func__);
264
0
        return;
265
0
    }
266
267
0
    if ((gplot = *pgplot) == NULL)
268
0
        return;
269
270
0
    LEPT_FREE(gplot->rootname);
271
0
    LEPT_FREE(gplot->cmdname);
272
0
    sarrayDestroy(&gplot->cmddata);
273
0
    sarrayDestroy(&gplot->datanames);
274
0
    sarrayDestroy(&gplot->plotdata);
275
0
    sarrayDestroy(&gplot->plotlabels);
276
0
    numaDestroy(&gplot->plotstyles);
277
0
    LEPT_FREE(gplot->outname);
278
0
    if (gplot->title)
279
0
        LEPT_FREE(gplot->title);
280
0
    if (gplot->xlabel)
281
0
        LEPT_FREE(gplot->xlabel);
282
0
    if (gplot->ylabel)
283
0
        LEPT_FREE(gplot->ylabel);
284
285
0
    LEPT_FREE(gplot);
286
0
    *pgplot = NULL;
287
0
}
288
289
290
/*!
291
 * \brief   gplotAddPlot()
292
 *
293
 * \param[in]    gplot
294
 * \param[in]    nax         [optional] numa: set to null for Y_VS_I;
295
 *                           required for Y_VS_X
296
 * \param[in]    nay         numa; required for both Y_VS_I and Y_VS_X
297
 * \param[in]    plotstyle   GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
298
 *                           GPLOT_LINESPOINTS, GPLOT_DOTS
299
 * \param[in]    plotlabel   [optional] label for individual plot
300
 * \return  0 if OK, 1 on error
301
 *
302
 * <pre>
303
 * Notes:
304
 *      (1) There are 2 options for (x,y) values:
305
 *            o  To plot an array vs a linear function of the
306
 *               index, set %nax = NULL.
307
 *            o  To plot one array vs another, use both %nax and %nay.
308
 *      (2) If %nax is NULL, the x value corresponding to the i-th
309
 *          value of %nay is found from the startx and delx fields
310
 *          in %nay:
311
 *               x = startx + i * delx
312
 *          These are set with numaSetParameters().  Their default
313
 *          values are startx = 0.0, delx = 1.0.
314
 *      (3) If %nax is defined, it must be the same size as %nay, and
315
 *          must have at least one number.
316
 *      (4) The 'plotlabel' string can have spaces, double
317
 *          quotes and backquotes, but not single quotes.
318
 * </pre>
319
 */
320
l_ok
321
gplotAddPlot(GPLOT       *gplot,
322
             NUMA        *nax,
323
             NUMA        *nay,
324
             l_int32      plotstyle,
325
             const char  *plotlabel)
326
0
{
327
0
char       buf[Bufsize];
328
0
char       emptystring[] = "";
329
0
char      *datastr, *title;
330
0
l_int32    n, i;
331
0
l_float32  valx, valy, startx, delx;
332
0
SARRAY    *sa;
333
334
0
    if (!gplot)
335
0
        return ERROR_INT("gplot not defined", __func__, 1);
336
0
    if (!nay)
337
0
        return ERROR_INT("nay not defined", __func__, 1);
338
0
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
339
0
        return ERROR_INT("invalid plotstyle", __func__, 1);
340
341
0
    if ((n = numaGetCount(nay)) == 0)
342
0
        return ERROR_INT("no points to plot", __func__, 1);
343
0
    if (nax && (n != numaGetCount(nax)))
344
0
        return ERROR_INT("nax and nay sizes differ", __func__, 1);
345
0
    if (n == 1 && plotstyle == GPLOT_LINES) {
346
0
        L_INFO("only 1 pt; changing style to points\n", __func__);
347
0
        plotstyle = GPLOT_POINTS;
348
0
    }
349
350
        /* Save plotstyle and plotlabel */
351
0
    numaGetParameters(nay, &startx, &delx);
352
0
    numaAddNumber(gplot->plotstyles, plotstyle);
353
0
    if (plotlabel) {
354
0
        title = stringNew(plotlabel);
355
0
        sarrayAddString(gplot->plotlabels, title, L_INSERT);
356
0
    } else {
357
0
        sarrayAddString(gplot->plotlabels, emptystring, L_COPY);
358
0
    }
359
360
        /* Generate and save data filename */
361
0
    gplot->nplots++;
362
0
    snprintf(buf, Bufsize, "%s.data.%d", gplot->rootname, gplot->nplots);
363
0
    sarrayAddString(gplot->datanames, buf, L_COPY);
364
365
        /* Generate data and save as a string */
366
0
    sa = sarrayCreate(n);
367
0
    for (i = 0; i < n; i++) {
368
0
        if (nax)
369
0
            numaGetFValue(nax, i, &valx);
370
0
        else
371
0
            valx = startx + i * delx;
372
0
        numaGetFValue(nay, i, &valy);
373
0
        snprintf(buf, Bufsize, "%f %f\n", valx, valy);
374
0
        sarrayAddString(sa, buf, L_COPY);
375
0
    }
376
0
    datastr = sarrayToString(sa, 0);
377
0
    sarrayAddString(gplot->plotdata, datastr, L_INSERT);
378
0
    sarrayDestroy(&sa);
379
380
0
    return 0;
381
0
}
382
383
384
/*!
385
 * \brief   gplotSetScaling()
386
 *
387
 * \param[in]    gplot
388
 * \param[in]    scaling   GPLOT_LINEAR_SCALE, GPLOT_LOG_SCALE_X,
389
 *                         GPLOT_LOG_SCALE_Y, GPLOT_LOG_SCALE_X_Y
390
 * \return  0 if OK; 1 on error
391
 *
392
 * <pre>
393
 * Notes:
394
 *      (1) By default, the x and y axis scaling is linear.
395
 *      (2) Call this function to set semi-log or log-log scaling.
396
 * </pre>
397
 */
398
l_ok
399
gplotSetScaling(GPLOT   *gplot,
400
                l_int32  scaling)
401
0
{
402
0
    if (!gplot)
403
0
        return ERROR_INT("gplot not defined", __func__, 1);
404
0
    if (scaling != GPLOT_LINEAR_SCALE &&
405
0
        scaling != GPLOT_LOG_SCALE_X &&
406
0
        scaling != GPLOT_LOG_SCALE_Y &&
407
0
        scaling != GPLOT_LOG_SCALE_X_Y)
408
0
        return ERROR_INT("invalid gplot scaling", __func__, 1);
409
0
    gplot->scaling = scaling;
410
0
    return 0;
411
0
}
412
413
414
/*!
415
 * \brief   gplotMakeOutputPix()
416
 *
417
 * \param[in]    gplot
418
 * \return  0 if OK; 1 on error
419
 *
420
 * <pre>
421
 * Notes:
422
 *      (1) This wraps gplotMakeOutput(), and returns a pix.
423
 *          See gplotMakeOutput() for details.
424
 *      (2) The gplot output format must be an image (png or pnm).
425
 * </pre>
426
 */
427
PIX *
428
gplotMakeOutputPix(GPLOT  *gplot)
429
0
{
430
0
    if (!gplot)
431
0
        return (PIX *)ERROR_PTR("gplot not defined", __func__, NULL);
432
0
    if (gplot->outformat != GPLOT_PNG && gplot->outformat != GPLOT_PNM)
433
0
        return (PIX *)ERROR_PTR("output format not an image", __func__, NULL);
434
435
0
    if (gplotMakeOutput(gplot))
436
0
        return (PIX *)ERROR_PTR("plot output not made", __func__, NULL);
437
0
    return pixRead(gplot->outname);
438
0
}
439
440
441
/*!
442
 * \brief   gplotMakeOutput()
443
 *
444
 * \param[in]    gplot
445
 * \return  0 if OK; 1 on error
446
 *
447
 * <pre>
448
 * Notes:
449
 *      (1) This uses gplot and the new arrays to add a plot
450
 *          to the output, by writing a new data file and appending
451
 *          the appropriate plot commands to the command file.
452
 *      (2) Along with gplotMakeOutputPix(), these are the only functions
453
 *          in this file that requires the gnuplot executable to
454
 *          actually generate the plot.
455
 *      (3) The command file name for unix is canonical (i.e., directory /tmp)
456
 *          but the temp filename paths in the command file must be correct.
457
 *      (4) The gnuplot program for Windows is wgnuplot.exe.
458
 * </pre>
459
 */
460
l_ok
461
gplotMakeOutput(GPLOT  *gplot)
462
0
{
463
0
char     buf[Bufsize];
464
0
char    *cmdname;
465
466
0
    if (!gplot)
467
0
        return ERROR_INT("gplot not defined", __func__, 1);
468
469
0
    if (!LeptDebugOK) {
470
0
        L_INFO("running gnuplot is disabled; "
471
0
               "use setLeptDebugOK(1) to enable\n", __func__);
472
0
        return 0;
473
0
    }
474
475
#ifdef OS_IOS /* iOS 11 does not support system() */
476
    return ERROR_INT("iOS 11 does not support system()", __func__, 0);
477
#endif /* OS_IOS */
478
479
0
    gplotGenCommandFile(gplot);
480
0
    gplotGenDataFiles(gplot);
481
0
    cmdname = genPathname(gplot->cmdname, NULL);
482
483
0
#ifndef _WIN32
484
0
    snprintf(buf, Bufsize, "gnuplot %s", cmdname);
485
#else
486
    snprintf(buf, Bufsize, "wgnuplot %s", cmdname);
487
#endif  /* _WIN32 */
488
489
0
    callSystemDebug(buf);  /* gnuplot || wgnuplot */
490
0
    LEPT_FREE(cmdname);
491
0
    return 0;
492
0
}
493
494
495
/*!
496
 * \brief   gplotGenCommandFile()
497
 *
498
 * \param[in]    gplot
499
 * \return  0 if OK, 1 on error
500
 */
501
l_ok
502
gplotGenCommandFile(GPLOT  *gplot)
503
0
{
504
0
char     buf[Bufsize];
505
0
char    *cmdstr, *plotlabel, *dataname;
506
0
l_int32  i, plotstyle, nplots;
507
0
FILE    *fp;
508
509
0
    if (!gplot)
510
0
        return ERROR_INT("gplot not defined", __func__, 1);
511
512
        /* Remove any previous command data */
513
0
    sarrayClear(gplot->cmddata);
514
515
        /* Generate command data instructions */
516
0
    if (gplot->title) {   /* set title */
517
0
        snprintf(buf, Bufsize, "set title '%s'", gplot->title);
518
0
        sarrayAddString(gplot->cmddata, buf, L_COPY);
519
0
    }
520
0
    if (gplot->xlabel) {   /* set xlabel */
521
0
        snprintf(buf, Bufsize, "set xlabel '%s'", gplot->xlabel);
522
0
        sarrayAddString(gplot->cmddata, buf, L_COPY);
523
0
    }
524
0
    if (gplot->ylabel) {   /* set ylabel */
525
0
        snprintf(buf, Bufsize, "set ylabel '%s'", gplot->ylabel);
526
0
        sarrayAddString(gplot->cmddata, buf, L_COPY);
527
0
    }
528
529
        /* Set terminal type and output */
530
0
    if (gplot->outformat == GPLOT_PNG) {
531
0
        snprintf(buf, Bufsize, "set terminal png; set output '%s'",
532
0
                 gplot->outname);
533
0
    } else if (gplot->outformat == GPLOT_PS) {
534
0
        snprintf(buf, Bufsize, "set terminal postscript; set output '%s'",
535
0
                 gplot->outname);
536
0
    } else if (gplot->outformat == GPLOT_EPS) {
537
0
        snprintf(buf, Bufsize, "set terminal postscript eps; set output '%s'",
538
0
                 gplot->outname);
539
0
    } else if (gplot->outformat == GPLOT_LATEX) {
540
0
        snprintf(buf, Bufsize, "set terminal latex; set output '%s'",
541
0
                 gplot->outname);
542
0
    } else if (gplot->outformat == GPLOT_PNM) {
543
0
        snprintf(buf, Bufsize, "set terminal pbm color; set output '%s'",
544
0
                 gplot->outname);
545
0
    }
546
0
    sarrayAddString(gplot->cmddata, buf, L_COPY);
547
548
0
    if (gplot->scaling == GPLOT_LOG_SCALE_X ||
549
0
        gplot->scaling == GPLOT_LOG_SCALE_X_Y) {
550
0
        snprintf(buf, Bufsize, "set logscale x");
551
0
        sarrayAddString(gplot->cmddata, buf, L_COPY);
552
0
    }
553
0
    if (gplot->scaling == GPLOT_LOG_SCALE_Y ||
554
0
        gplot->scaling == GPLOT_LOG_SCALE_X_Y) {
555
0
        snprintf(buf, Bufsize, "set logscale y");
556
0
        sarrayAddString(gplot->cmddata, buf, L_COPY);
557
0
    }
558
559
0
    nplots = sarrayGetCount(gplot->datanames);
560
0
    for (i = 0; i < nplots; i++) {
561
0
        plotlabel = sarrayGetString(gplot->plotlabels, i, L_NOCOPY);
562
0
        dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY);
563
0
        numaGetIValue(gplot->plotstyles, i, &plotstyle);
564
0
        if (nplots == 1) {
565
0
            snprintf(buf, Bufsize, "plot '%s' title '%s' %s",
566
0
                     dataname, plotlabel, gplotstylenames[plotstyle]);
567
0
        } else {
568
0
            if (i == 0)
569
0
                snprintf(buf, Bufsize, "plot '%s' title '%s' %s, \\",
570
0
                     dataname, plotlabel, gplotstylenames[plotstyle]);
571
0
            else if (i < nplots - 1)
572
0
                snprintf(buf, Bufsize, " '%s' title '%s' %s, \\",
573
0
                     dataname, plotlabel, gplotstylenames[plotstyle]);
574
0
            else
575
0
                snprintf(buf, Bufsize, " '%s' title '%s' %s",
576
0
                     dataname, plotlabel, gplotstylenames[plotstyle]);
577
0
        }
578
0
        sarrayAddString(gplot->cmddata, buf, L_COPY);
579
0
    }
580
581
        /* Write command data to file */
582
0
    cmdstr = sarrayToString(gplot->cmddata, 1);
583
0
    if ((fp = fopenWriteStream(gplot->cmdname, "w")) == NULL) {
584
0
        L_ERROR("stream not opened for command: %s\n", __func__, cmdstr);
585
0
        LEPT_FREE(cmdstr);
586
0
        return 1;
587
0
    }
588
0
    fwrite(cmdstr, 1, strlen(cmdstr), fp);
589
0
    fclose(fp);
590
0
    LEPT_FREE(cmdstr);
591
0
    return 0;
592
0
}
593
594
595
/*!
596
 * \brief   gplotGenDataFiles()
597
 *
598
 * \param[in]    gplot
599
 * \return  0 if OK, 1 on error
600
 *
601
 * <pre>
602
 * Notes:
603
 *      (1) The pathnames in the gplot command file are actual pathnames,
604
 *          which can be in temp directories.  Consequently, they must not be
605
 *          rewritten by calling fopenWriteStream(), and we use fopen().
606
 * </pre>
607
 */
608
l_ok
609
gplotGenDataFiles(GPLOT  *gplot)
610
0
{
611
0
char    *plotdata, *dataname;
612
0
l_int32  i, nplots;
613
0
FILE    *fp;
614
615
0
    if (!gplot)
616
0
        return ERROR_INT("gplot not defined", __func__, 1);
617
618
0
    nplots = sarrayGetCount(gplot->datanames);
619
0
    for (i = 0; i < nplots; i++) {
620
0
        plotdata = sarrayGetString(gplot->plotdata, i, L_NOCOPY);
621
0
        dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY);
622
0
        if ((fp = fopen(dataname, "w")) == NULL)
623
0
            return ERROR_INT_1("datafile stream not opened",
624
0
                               dataname, __func__, 1);
625
0
        fwrite(plotdata, 1, strlen(plotdata), fp);
626
0
        fclose(fp);
627
0
    }
628
629
0
    return 0;
630
0
}
631
632
633
/*-----------------------------------------------------------------*
634
 *                        Quick one-line plots                     *
635
 *-----------------------------------------------------------------*/
636
/*!
637
 * \brief   gplotSimple1()
638
 *
639
 * \param[in]    na          numa; plot Y_VS_I
640
 * \param[in]    outformat   GPLOT_PNG, GPLOT_PS, GPLOT_EPS,
641
 *                           GPLOT_LATEX, GPLOT_PNM
642
 * \param[in]    outroot     root of output files
643
 * \param[in]    title       [optional], can be NULL
644
 * \return  0 if OK, 1 on error
645
 *
646
 * <pre>
647
 * Notes:
648
 *      (1) This generates a line plot of a numa, where the array value
649
 *          is plotted vs the array index.  The plot is generated
650
 *          in the specified output format; the title  is optional.
651
 *      (2) When calling these simple plot functions more than once, use
652
 *          different %outroot to avoid overwriting the output files.
653
 * </pre>
654
 */
655
l_ok
656
gplotSimple1(NUMA        *na,
657
             l_int32      outformat,
658
             const char  *outroot,
659
             const char  *title)
660
0
{
661
0
GPLOT  *gplot;
662
663
0
    gplot = gplotSimpleXY1(NULL, na, GPLOT_LINES, outformat, outroot, title);
664
0
    if (!gplot)
665
0
        return ERROR_INT("failed to generate plot", __func__, 1);
666
0
    gplotDestroy(&gplot);
667
0
    return 0;
668
0
}
669
670
671
/*!
672
 * \brief   gplotSimple2()
673
 *
674
 * \param[in]    na1         numa; plot with Y_VS_I
675
 * \param[in]    na2         ditto
676
 * \param[in]    outformat   GPLOT_PNG, GPLOT_PS, GPLOT_EPS,
677
 *                           GPLOT_LATEX, GPLOT_PNM
678
 * \param[in]    outroot     root of output files
679
 * \param[in]    title       [optional]
680
 * \return  0 if OK, 1 on error
681
 *
682
 * <pre>
683
 * Notes:
684
 *      (1) This generates a line plot of two numa, where the array values
685
 *          are each plotted vs the array index.  The plot is generated
686
 *          in the specified output format; the title  is optional.
687
 *      (2) When calling these simple plot functions more than once, use
688
 *          different %outroot to avoid overwriting the output files.
689
 * </pre>
690
 */
691
l_ok
692
gplotSimple2(NUMA        *na1,
693
             NUMA        *na2,
694
             l_int32      outformat,
695
             const char  *outroot,
696
             const char  *title)
697
0
{
698
0
GPLOT  *gplot;
699
700
0
    gplot = gplotSimpleXY2(NULL, na1, na2, GPLOT_LINES,
701
0
                           outformat, outroot, title);
702
0
    if (!gplot)
703
0
        return ERROR_INT("failed to generate plot", __func__, 1);
704
0
    gplotDestroy(&gplot);
705
0
    return 0;
706
0
}
707
708
709
/*!
710
 * \brief   gplotSimpleN()
711
 *
712
 * \param[in]    naa         numaa; plot Y_VS_I for each numa
713
 * \param[in]    outformat   GPLOT_PNG, GPLOT_PS, GPLOT_EPS,
714
 *                           GPLOT_LATEX, GPLOT_PNM
715
 * \param[in]    outroot     root of output files
716
 * \param[in]    title       [optional]
717
 * \return  0 if OK, 1 on error
718
 *
719
 * <pre>
720
 * Notes:
721
 *      (1) This generates a line plot of all numas in a numaa (array of numa),
722
 *          where the array values are each plotted vs the array index.
723
 *          The plot is generated in the specified output format;
724
 *          the title  is optional.
725
 *      (2) When calling these simple plot functions more than once, use
726
 *          different %outroot to avoid overwriting the output files.
727
 * </pre>
728
 */
729
l_ok
730
gplotSimpleN(NUMAA       *naa,
731
             l_int32      outformat,
732
             const char  *outroot,
733
             const char  *title)
734
0
{
735
0
GPLOT  *gplot;
736
737
0
    gplot = gplotSimpleXYN(NULL, naa, GPLOT_LINES, outformat, outroot, title);
738
0
    if (!gplot)
739
0
        return ERROR_INT("failed to generate plot", __func__, 1);
740
0
    gplotDestroy(&gplot);
741
0
    return 0;
742
0
}
743
744
745
/*!
746
 * \brief   gplotSimplePix1()
747
 *
748
 * \param[in]    na          numa; plot Y_VS_I
749
 * \param[in]    title       [optional], can be NULL
750
 * \return  pix   of plot, or null on error
751
 *
752
 * <pre>
753
 * Notes:
754
 *      (1) This generates a line plot of a numa as a pix, where the array
755
 *          value is plotted vs the array index.  The title is optional.
756
 *      (2) The temporary plot file is a png; its name is generated internally
757
 *          and stored in gplot.
758
 * </pre>
759
 */
760
PIX *
761
gplotSimplePix1(NUMA        *na,
762
                const char  *title)
763
0
{
764
0
char            buf[64];
765
0
static l_atomic index;
766
0
GPLOT          *gplot;
767
0
PIX            *pix;
768
769
0
    if (!na)
770
0
        return (PIX *)ERROR_PTR("na not defined", __func__, NULL);
771
772
0
    lept_mkdir("lept/gplot/pix");
773
0
    snprintf(buf, sizeof(buf), "/tmp/lept/gplot/pix1.%d", index++);
774
0
    gplot = gplotSimpleXY1(NULL, na, GPLOT_LINES, GPLOT_PNG, buf, title);
775
0
    if (!gplot)
776
0
        return (PIX *)ERROR_PTR("failed to generate plot", __func__, NULL);
777
0
    pix = pixRead(gplot->outname);
778
0
    gplotDestroy(&gplot);
779
0
    if (!pix)
780
0
        return (PIX *)ERROR_PTR("failed to generate plot", __func__, NULL);
781
0
    return pix;
782
0
}
783
784
785
/*!
786
 * \brief   gplotSimplePix2()
787
 *
788
 * \param[in]    na1         numa; plot with Y_VS_I
789
 * \param[in]    na2         ditto
790
 * \param[in]    title       [optional], can be NULL
791
 * \return  pix   of plot, or null on error
792
 *
793
 * <pre>
794
 * Notes:
795
 *      (1) This generates a pix with line plots of two numa, where each of
796
 *          two arrays is plotted vs the array index.  the title is optional.
797
 *      (2) The temporary plot file is a png; its name is generated internally
798
 *          and stored in gplot.
799
 * </pre>
800
 */
801
PIX *
802
gplotSimplePix2(NUMA        *na1,
803
                NUMA        *na2,
804
                const char  *title)
805
0
{
806
0
char            buf[64];
807
0
static l_atomic index;
808
0
GPLOT          *gplot;
809
0
PIX            *pix;
810
811
0
    if (!na1 || !na2)
812
0
        return (PIX *)ERROR_PTR("both na1, na2 not defined", __func__, NULL);
813
814
0
    lept_mkdir("lept/gplot/pix");
815
0
    snprintf(buf, sizeof(buf), "/tmp/lept/gplot/pix2.%d", index++);
816
0
    gplot = gplotSimpleXY2(NULL, na1, na2, GPLOT_LINES, GPLOT_PNG, buf, title);
817
0
    if (!gplot)
818
0
        return (PIX *)ERROR_PTR("failed to generate plot", __func__, NULL);
819
0
    pix = pixRead(gplot->outname);
820
0
    gplotDestroy(&gplot);
821
0
    if (!pix)
822
0
        return (PIX *)ERROR_PTR("failed to generate plot", __func__, NULL);
823
0
    return pix;
824
0
}
825
826
827
/*!
828
 * \brief   gplotSimplePixN()
829
 *
830
 * \param[in]    naa         numaa; plot Y_VS_I for each numa
831
 * \param[in]    title       [optional], can be NULL
832
 * \return  pix   of plot, or null on error
833
 *
834
 * <pre>
835
 * Notes:
836
 *      (1) This generates a pix with an arbitrary number of line plots,
837
 *          each coming from a numa in %naa.  Each array value is plotted
838
 *          vs the array index.  The title is optional.
839
 *      (2) The temporary plot file is a png; its name is generated internally
840
 *          and stored in gplot.
841
 * </pre>
842
 */
843
PIX *
844
gplotSimplePixN(NUMAA       *naa,
845
                const char  *title)
846
0
{
847
0
char            buf[64];
848
0
static l_atomic index;
849
0
GPLOT          *gplot;
850
0
PIX            *pix;
851
852
0
    if (!naa)
853
0
        return (PIX *)ERROR_PTR("naa not defined", __func__, NULL);
854
855
0
    lept_mkdir("lept/gplot/pix");
856
0
    snprintf(buf, sizeof(buf), "/tmp/lept/gplot/pixN.%d", index++);
857
0
    gplot = gplotSimpleXYN(NULL, naa, GPLOT_LINES, GPLOT_PNG, buf, title);
858
0
    if (!gplot)
859
0
        return (PIX *)ERROR_PTR("failed to generate plot", __func__, NULL);
860
0
    pix = pixRead(gplot->outname);
861
0
    gplotDestroy(&gplot);
862
0
    if (!pix)
863
0
        return (PIX *)ERROR_PTR("failed to generate plot", __func__, NULL);
864
0
    return pix;
865
0
}
866
867
868
/*!
869
 * \brief   gplotSimpleXY1()
870
 *
871
 * \param[in]    nax         [optional]
872
 * \param[in]    nay         [required]
873
 * \param[in]    plotstyle   GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
874
 *                           GPLOT_LINESPOINTS, GPLOT_DOTS
875
 * \param[in]    outformat   GPLOT_PNG, GPLOT_PS, GPLOT_EPS,
876
 *                           GPLOT_LATEX, GPLOT_PNM
877
 * \param[in]    outroot     root of output files
878
 * \param[in]    title       [optional], can be NULL
879
 * \return  gplot   or null on error
880
 *
881
 * <pre>
882
 * Notes:
883
 *      (1) This generates a plot of a %nay vs %nax, generated in
884
 *          the specified output format.  The title is optional.
885
 *      (2) Use 0 for default plotstyle (lines).
886
 *      (3) %nax is optional.  If NULL, %nay is plotted against
887
 *          the array index.
888
 *      (4) When calling these simple plot functions more than once, use
889
 *          different %outroot to avoid overwriting the output files.
890
 *      (5) The returned gplot must be destroyed by the caller.
891
 * </pre>
892
 */
893
GPLOT *
894
gplotSimpleXY1(NUMA        *nax,
895
               NUMA        *nay,
896
               l_int32      plotstyle,
897
               l_int32      outformat,
898
               const char  *outroot,
899
               const char  *title)
900
0
{
901
0
GPLOT  *gplot;
902
903
0
    if (!nay)
904
0
        return (GPLOT *)ERROR_PTR("nay not defined", __func__, NULL);
905
0
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
906
0
        return (GPLOT *)ERROR_PTR("invalid plotstyle", __func__, NULL);
907
0
    if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
908
0
        outformat != GPLOT_EPS && outformat != GPLOT_LATEX &&
909
0
        outformat != GPLOT_PNM)
910
0
        return (GPLOT *)ERROR_PTR("invalid outformat", __func__, NULL);
911
0
    if (!outroot)
912
0
        return (GPLOT *)ERROR_PTR("outroot not specified", __func__, NULL);
913
914
0
    if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0)
915
0
        return (GPLOT *)ERROR_PTR("gplot not made", __func__, NULL);
916
0
    gplotAddPlot(gplot, nax, nay, plotstyle, NULL);
917
0
    gplotMakeOutput(gplot);
918
0
    return gplot;
919
0
}
920
921
922
/*!
923
 * \brief   gplotSimpleXY2()
924
 *
925
 * \param[in]    nax          [optional], can be NULL
926
 * \param[in]    nay1
927
 * \param[in]    nay2
928
 * \param[in]    plotstyle    GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
929
 *                            GPLOT_LINESPOINTS, GPLOT_DOTS
930
 * \param[in]    outformat    GPLOT_PNG, GPLOT_PS, GPLOT_EPS,
931
 *                            GPLOT_LATEX, GPLOT_PNM
932
 * \param[in]    outroot      root of output files
933
 * \param[in]    title        [optional]
934
 * \return  gplot   or null on error
935
 *
936
 * <pre>
937
 * Notes:
938
 *      (1) This generates plots of %nay1 and %nay2 against %nax, generated
939
 *          in the specified output format.  The title is optional.
940
 *      (2) Use 0 for default plotstyle (lines).
941
 *      (3) %nax is optional.  If NULL, %nay1 and %nay2 are plotted
942
 *          against the array index.
943
 *      (4) When calling these simple plot functions more than once, use
944
 *          different %outroot to avoid overwriting the output files.
945
 *      (5) The returned gplot must be destroyed by the caller.
946
 * </pre>
947
 */
948
GPLOT *
949
gplotSimpleXY2(NUMA        *nax,
950
               NUMA        *nay1,
951
               NUMA        *nay2,
952
               l_int32      plotstyle,
953
               l_int32      outformat,
954
               const char  *outroot,
955
               const char  *title)
956
0
{
957
0
GPLOT  *gplot;
958
959
0
    if (!nay1 || !nay2)
960
0
        return (GPLOT *)ERROR_PTR("nay1 and nay2 not both defined",
961
0
                                  __func__, NULL);
962
0
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
963
0
        return (GPLOT *)ERROR_PTR("invalid plotstyle", __func__, NULL);
964
0
    if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
965
0
        outformat != GPLOT_EPS && outformat != GPLOT_LATEX &&
966
0
        outformat != GPLOT_PNM)
967
0
        return (GPLOT *)ERROR_PTR("invalid outformat", __func__, NULL);
968
0
    if (!outroot)
969
0
        return (GPLOT *)ERROR_PTR("outroot not specified", __func__, NULL);
970
971
0
    if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0)
972
0
        return (GPLOT *)ERROR_PTR("gplot not made", __func__, NULL);
973
0
    gplotAddPlot(gplot, nax, nay1, plotstyle, NULL);
974
0
    gplotAddPlot(gplot, nax, nay2, plotstyle, NULL);
975
0
    gplotMakeOutput(gplot);
976
0
    return gplot;
977
0
}
978
979
980
/*!
981
 * \brief   gplotSimpleXYN()
982
 *
983
 * \param[in]    nax          [optional]; can be NULL
984
 * \param[in]    naay         numaa of arrays to plot against %nax
985
 * \param[in]    plotstyle    GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
986
 *                            GPLOT_LINESPOINTS, GPLOT_DOTS
987
 * \param[in]    outformat    GPLOT_PNG, GPLOT_PS, GPLOT_EPS,
988
 *                            GPLOT_LATEX, GPLOT_PNM
989
 * \param[in]    outroot      root of output files
990
 * \param[in]    title        [optional]
991
 * \return  gplot  or null on error
992
 *
993
 * <pre>
994
 * Notes:
995
 *      (1) This generates plots of each Numa in %naa against %nax,
996
 *          generated in the specified output format.  The title is optional.
997
 *      (2) Use 0 for default plotstyle (lines).
998
 *      (3) %nax is optional.  If NULL, each Numa array is plotted against
999
 *          the array index.
1000
 *      (4) When calling these simple plot functions more than once, use
1001
 *          different %outroot to avoid overwriting the output files.
1002
 *      (5) The returned gplot must be destroyed by the caller.
1003
 * </pre>
1004
 */
1005
GPLOT *
1006
gplotSimpleXYN(NUMA        *nax,
1007
               NUMAA       *naay,
1008
               l_int32      plotstyle,
1009
               l_int32      outformat,
1010
               const char  *outroot,
1011
               const char  *title)
1012
0
{
1013
0
l_int32  i, n;
1014
0
GPLOT   *gplot;
1015
0
NUMA    *nay;
1016
1017
0
    if (!naay)
1018
0
        return (GPLOT *)ERROR_PTR("naay not defined", __func__, NULL);
1019
0
    if ((n = numaaGetCount(naay)) == 0)
1020
0
        return (GPLOT *)ERROR_PTR("no numa in array", __func__, NULL);
1021
0
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
1022
0
        return (GPLOT *)ERROR_PTR("invalid plotstyle", __func__, NULL);
1023
0
    if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
1024
0
        outformat != GPLOT_EPS && outformat != GPLOT_LATEX &&
1025
0
        outformat != GPLOT_PNM)
1026
0
        return (GPLOT *)ERROR_PTR("invalid outformat", __func__, NULL);
1027
0
    if (!outroot)
1028
0
        return (GPLOT *)ERROR_PTR("outroot not specified", __func__, NULL);
1029
1030
0
    if ((gplot = gplotCreate(outroot, outformat, title, NULL, NULL)) == 0)
1031
0
        return (GPLOT *)ERROR_PTR("gplot not made", __func__, NULL);
1032
0
    for (i = 0; i < n; i++) {
1033
0
        nay = numaaGetNuma(naay, i, L_CLONE);
1034
0
        gplotAddPlot(gplot, nax, nay, plotstyle, NULL);
1035
0
        numaDestroy(&nay);
1036
0
    }
1037
0
    gplotMakeOutput(gplot);
1038
0
    return gplot;
1039
0
}
1040
1041
1042
/*!
1043
 * \brief   gplotGeneralPix1()
1044
 *
1045
 * \param[in]    na          data array
1046
 * \param[in]    plotstyle   GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
1047
 *                           GPLOT_LINESPOINTS, GPLOT_DOTS
1048
 * \param[in]    rootname    root for all output files
1049
 * \param[in]    title       [optional] overall title
1050
 * \param[in]    xlabel      [optional] x axis label
1051
 * \param[in]    ylabel      [optional] y axis label
1052
 * \return  pix   of plot, or NULL on error
1053
 *
1054
 * <pre>
1055
 * Notes:
1056
 *      (1) The 'title', 'xlabel' and 'ylabel' strings can have spaces,
1057
 *          double quotes and backquotes, but not single quotes.
1058
 * </pre>
1059
 */
1060
PIX *
1061
gplotGeneralPix1(NUMA        *na,
1062
                 l_int32      plotstyle,
1063
                 const char  *rootname,
1064
                 const char  *title,
1065
                 const char  *xlabel,
1066
                 const char  *ylabel)
1067
0
{
1068
0
GPLOT *gplot;
1069
0
PIX   *pix;
1070
1071
0
    if (!na)
1072
0
        return (PIX *)ERROR_PTR("na not defined", __func__, NULL);
1073
0
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
1074
0
        return (PIX *)ERROR_PTR("invalid plotstyle", __func__, NULL);
1075
0
    if (!rootname)
1076
0
        return (PIX *)ERROR_PTR("rootname not defined", __func__, NULL);
1077
1078
0
    gplot = gplotCreate(rootname, GPLOT_PNG, title, xlabel, ylabel);
1079
0
    if (!gplot)
1080
0
        return (PIX *)ERROR_PTR("gplot not made", __func__, NULL);
1081
0
    gplotAddPlot(gplot, NULL, na, plotstyle, NULL);
1082
0
    pix = gplotMakeOutputPix(gplot);
1083
0
    gplotDestroy(&gplot);
1084
0
    return pix;
1085
0
}
1086
1087
1088
/*!
1089
 * \brief   gplotGeneralPix2()
1090
 *
1091
 * \param[in]    na1         x-axis data array
1092
 * \param[in]    na2         y-axis data array
1093
 * \param[in]    plotstyle   GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
1094
 *                           GPLOT_LINESPOINTS, GPLOT_DOTS
1095
 * \param[in]    rootname    root for all output files
1096
 * \param[in]    title       [optional] overall title
1097
 * \param[in]    xlabel      [optional] x axis label
1098
 * \param[in]    ylabel      [optional] y axis label
1099
 * \return  pix   of plot, or NULL on error
1100
 *
1101
 * <pre>
1102
 * Notes:
1103
 *      (1) The 'title', 'xlabel' and 'ylabel' strings can have spaces,
1104
 *          double quotes and backquotes, but not single quotes.
1105
 * </pre>
1106
 */
1107
PIX *
1108
gplotGeneralPix2(NUMA        *na1,
1109
                 NUMA        *na2,
1110
                 l_int32      plotstyle,
1111
                 const char  *rootname,
1112
                 const char  *title,
1113
                 const char  *xlabel,
1114
                 const char  *ylabel)
1115
0
{
1116
0
GPLOT *gplot;
1117
0
PIX   *pix;
1118
1119
0
    if (!na1)
1120
0
        return (PIX *)ERROR_PTR("na1 not defined", __func__, NULL);
1121
0
    if (!na2)
1122
0
        return (PIX *)ERROR_PTR("na2 not defined", __func__, NULL);
1123
0
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
1124
0
        return (PIX *)ERROR_PTR("invalid plotstyle", __func__, NULL);
1125
0
    if (!rootname)
1126
0
        return (PIX *)ERROR_PTR("rootname not defined", __func__, NULL);
1127
1128
0
    gplot = gplotCreate(rootname, GPLOT_PNG, title, xlabel, ylabel);
1129
0
    if (!gplot)
1130
0
        return (PIX *)ERROR_PTR("gplot not made", __func__, NULL);
1131
0
    gplotAddPlot(gplot, na1, na2, plotstyle, NULL);
1132
0
    pix = gplotMakeOutputPix(gplot);
1133
0
    gplotDestroy(&gplot);
1134
0
    return pix;
1135
0
}
1136
1137
1138
/*!
1139
 * \brief   gplotGeneralPixN()
1140
 *
1141
 * \param[in]    nax         x-axis data array
1142
 * \param[in]    naay        array of y-axis data arrays
1143
 * \param[in]    plotstyle   GPLOT_LINES, GPLOT_POINTS, GPLOT_IMPULSES,
1144
 *                           GPLOT_LINESPOINTS, GPLOT_DOTS
1145
 * \param[in]    rootname    root for all output files
1146
 * \param[in]    title       [optional] overall title
1147
 * \param[in]    xlabel      [optional] x axis label
1148
 * \param[in]    ylabel      [optional] y axis label
1149
 * \return  pix   of plot, or NULL on error
1150
 *
1151
 * <pre>
1152
 * Notes:
1153
 *      (1) The 'title', 'xlabel' and 'ylabel' strings can have spaces,
1154
 *          double quotes and backquotes, but not single quotes.
1155
 * </pre>
1156
 */
1157
PIX *
1158
gplotGeneralPixN(NUMA        *nax,
1159
                 NUMAA       *naay,
1160
                 l_int32      plotstyle,
1161
                 const char  *rootname,
1162
                 const char  *title,
1163
                 const char  *xlabel,
1164
                 const char  *ylabel)
1165
0
{
1166
0
l_int32  i, n;
1167
0
GPLOT   *gplot;
1168
0
NUMA    *nay;
1169
0
PIX     *pix;
1170
1171
0
    if (!nax)
1172
0
        return (PIX *)ERROR_PTR("nax not defined", __func__, NULL);
1173
0
    if (!naay)
1174
0
        return (PIX *)ERROR_PTR("naay not defined", __func__, NULL);
1175
0
    if ((n = numaaGetCount(naay)) == 0)
1176
0
        return (PIX *)ERROR_PTR("no numa in array", __func__, NULL);
1177
0
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
1178
0
        return (PIX *)ERROR_PTR("invalid plotstyle", __func__, NULL);
1179
0
    if (!rootname)
1180
0
        return (PIX *)ERROR_PTR("rootname not defined", __func__, NULL);
1181
1182
0
    gplot = gplotCreate(rootname, GPLOT_PNG, title, xlabel, ylabel);
1183
0
    if (!gplot)
1184
0
        return (PIX *)ERROR_PTR("gplot not made", __func__, NULL);
1185
0
    for (i = 0; i < n; i++) {
1186
0
        nay = numaaGetNuma(naay, i, L_CLONE);
1187
0
        gplotAddPlot(gplot, nax, nay, plotstyle, NULL);
1188
0
        numaDestroy(&nay);
1189
0
    }
1190
0
    pix = gplotMakeOutputPix(gplot);
1191
0
    gplotDestroy(&gplot);
1192
0
    return pix;
1193
0
}
1194
1195
1196
/*-----------------------------------------------------------------*
1197
 *                           Serialize for I/O                     *
1198
 *-----------------------------------------------------------------*/
1199
/*!
1200
 * \brief   gplotRead()
1201
 *
1202
 * \param[in]    filename
1203
 * \return  gplot, or NULL on error
1204
 */
1205
GPLOT *
1206
gplotRead(const char  *filename)
1207
0
{
1208
0
char     buf[Bufsize];
1209
0
char    *rootname, *title, *xlabel, *ylabel, *ignores;
1210
0
l_int32  outformat, ret, version, ignore;
1211
0
FILE    *fp;
1212
0
GPLOT   *gplot;
1213
1214
0
    if (!filename)
1215
0
        return (GPLOT *)ERROR_PTR("filename not defined", __func__, NULL);
1216
1217
0
    if ((fp = fopenReadStream(filename)) == NULL)
1218
0
        return (GPLOT *)ERROR_PTR_1("stream not opened",
1219
0
                                    filename, __func__, NULL);
1220
1221
0
    ret = fscanf(fp, "Gplot Version %d\n", &version);
1222
0
    if (ret != 1) {
1223
0
        fclose(fp);
1224
0
        return (GPLOT *)ERROR_PTR_1("not a gplot file",
1225
0
                                    filename, __func__, NULL);
1226
0
    }
1227
0
    if (version != GPLOT_VERSION_NUMBER) {
1228
0
        fclose(fp);
1229
0
        return (GPLOT *)ERROR_PTR_1("invalid gplot version",
1230
0
                                    filename, __func__, NULL);
1231
0
    }
1232
1233
0
    ignore = fscanf(fp, "Rootname: %511s\n", buf);  /* Bufsize - 1 */
1234
0
    rootname = stringNew(buf);
1235
0
    ignore = fscanf(fp, "Output format: %d\n", &outformat);
1236
0
    ignores = fgets(buf, Bufsize, fp);   /* Title: ... */
1237
0
    title = stringNew(buf + 7);
1238
0
    title[strlen(title) - 1] = '\0';
1239
0
    ignores = fgets(buf, Bufsize, fp);   /* X axis label: ... */
1240
0
    xlabel = stringNew(buf + 14);
1241
0
    xlabel[strlen(xlabel) - 1] = '\0';
1242
0
    ignores = fgets(buf, Bufsize, fp);   /* Y axis label: ... */
1243
0
    ylabel = stringNew(buf + 14);
1244
0
    ylabel[strlen(ylabel) - 1] = '\0';
1245
1246
0
    gplot = gplotCreate(rootname, outformat, title, xlabel, ylabel);
1247
0
    LEPT_FREE(rootname);
1248
0
    LEPT_FREE(title);
1249
0
    LEPT_FREE(xlabel);
1250
0
    LEPT_FREE(ylabel);
1251
0
    if (!gplot) {
1252
0
        fclose(fp);
1253
0
        return (GPLOT *)ERROR_PTR_1("gplot not made", filename, __func__, NULL);
1254
0
    }
1255
0
    sarrayDestroy(&gplot->cmddata);
1256
0
    sarrayDestroy(&gplot->datanames);
1257
0
    sarrayDestroy(&gplot->plotdata);
1258
0
    sarrayDestroy(&gplot->plotlabels);
1259
0
    numaDestroy(&gplot->plotstyles);
1260
1261
0
    ignore = fscanf(fp, "Commandfile name: %s\n", buf);  /* Bufsize - 1 */
1262
0
    stringReplace(&gplot->cmdname, buf);
1263
0
    ignore = fscanf(fp, "\nCommandfile data:");
1264
0
    gplot->cmddata = sarrayReadStream(fp);
1265
0
    ignore = fscanf(fp, "\nDatafile names:");
1266
0
    gplot->datanames = sarrayReadStream(fp);
1267
0
    ignore = fscanf(fp, "\nPlot data:");
1268
0
    gplot->plotdata = sarrayReadStream(fp);
1269
0
    ignore = fscanf(fp, "\nPlot titles:");
1270
0
    gplot->plotlabels = sarrayReadStream(fp);
1271
0
    ignore = fscanf(fp, "\nPlot styles:");
1272
0
    gplot->plotstyles = numaReadStream(fp);
1273
1274
0
    ignore = fscanf(fp, "Number of plots: %d\n", &gplot->nplots);
1275
0
    ignore = fscanf(fp, "Output file name: %s\n", buf);
1276
0
    stringReplace(&gplot->outname, buf);
1277
0
    ignore = fscanf(fp, "Axis scaling: %d\n", &gplot->scaling);
1278
1279
0
    fclose(fp);
1280
0
    return gplot;
1281
0
}
1282
1283
1284
/*!
1285
 * \brief   gplotWrite()
1286
 *
1287
 * \param[in]    filename
1288
 * \param[in]    gplot
1289
 * \return  0 if OK; 1 on error
1290
 */
1291
l_ok
1292
gplotWrite(const char  *filename,
1293
           GPLOT       *gplot)
1294
0
{
1295
0
FILE  *fp;
1296
1297
0
    if (!filename)
1298
0
        return ERROR_INT("filename not defined", __func__, 1);
1299
0
    if (!gplot)
1300
0
        return ERROR_INT("gplot not defined", __func__, 1);
1301
1302
0
    if ((fp = fopenWriteStream(filename, "wb")) == NULL)
1303
0
        return ERROR_INT_1("stream not opened", filename, __func__, 1);
1304
1305
0
    fprintf(fp, "Gplot Version %d\n", GPLOT_VERSION_NUMBER);
1306
0
    fprintf(fp, "Rootname: %s\n", gplot->rootname);
1307
0
    fprintf(fp, "Output format: %d\n", gplot->outformat);
1308
0
    fprintf(fp, "Title: %s\n", gplot->title);
1309
0
    fprintf(fp, "X axis label: %s\n", gplot->xlabel);
1310
0
    fprintf(fp, "Y axis label: %s\n", gplot->ylabel);
1311
1312
0
    fprintf(fp, "Commandfile name: %s\n", gplot->cmdname);
1313
0
    fprintf(fp, "\nCommandfile data:");
1314
0
    sarrayWriteStream(fp, gplot->cmddata);
1315
0
    fprintf(fp, "\nDatafile names:");
1316
0
    sarrayWriteStream(fp, gplot->datanames);
1317
0
    fprintf(fp, "\nPlot data:");
1318
0
    sarrayWriteStream(fp, gplot->plotdata);
1319
0
    fprintf(fp, "\nPlot titles:");
1320
0
    sarrayWriteStream(fp, gplot->plotlabels);
1321
0
    fprintf(fp, "\nPlot styles:");
1322
0
    numaWriteStderr(gplot->plotstyles);
1323
1324
0
    fprintf(fp, "Number of plots: %d\n", gplot->nplots);
1325
0
    fprintf(fp, "Output file name: %s\n", gplot->outname);
1326
0
    fprintf(fp, "Axis scaling: %d\n", gplot->scaling);
1327
1328
0
    fclose(fp);
1329
0
    return 0;
1330
0
}