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