1 /*
2 * gretl -- Gnu Regression, Econometrics and Time-series Library
3 * Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "libgretl.h"
21 #include "plotspec.h"
22
23 struct plotbars_ {
24 int n; /* number of available start-stop "bar" pairs */
25 double t1; /* current min. value on time axis */
26 double t2; /* current max. value on time axis */
27 double ymin; /* current min. value on y axis */
28 double ymax; /* current max. value on y axis */
29 double **dx; /* start-stop time pairs */
30 };
31
32 /* for handling "recession bars" or similar */
33 static int n_bars_shown (double xmin, double xmax, plotbars *bars);
34 static void print_bars_header (GPT_SPEC *spec, FILE *fp);
35 static void print_plotbars (GPT_SPEC *spec, FILE *fp);
36
37 static void print_filledcurve_color (FILE *fp);
38
plotspec_new(void)39 GPT_SPEC *plotspec_new (void)
40 {
41 GPT_SPEC *spec;
42 int i;
43
44 spec = malloc(sizeof *spec);
45 if (spec == NULL) {
46 return NULL;
47 }
48
49 spec->datacols = 0;
50 spec->heredata = 0;
51
52 spec->lines = NULL;
53 spec->n_lines = 0;
54
55 spec->labels = NULL;
56 spec->n_labels = 0;
57
58 spec->arrows = NULL;
59 spec->n_arrows = 0;
60
61 spec->literal = NULL;
62 spec->n_literal = 0;
63
64 for (i=0; i<5; i++) {
65 spec->titles[i] = NULL;
66 }
67
68 *spec->xvarname = '\0';
69 *spec->yvarname = '\0';
70
71 spec->xticstr = NULL;
72 spec->x2ticstr = NULL;
73
74 spec->timefmt[0] = 0;
75 spec->xfmt[0] = 0;
76 spec->xtics[0] = 0;
77 spec->mxtics[0] = 0;
78 spec->yfmt[0] = 0;
79 spec->ytics[0] = 0;
80 spec->fname[0] = 0;
81 spec->keyspec = GP_KEY_LEFT_TOP;
82
83 for (i=0; i<5; i++) {
84 spec->range[i][0] = NADBL;
85 spec->range[i][1] = NADBL;
86 if (i < 3) {
87 spec->logbase[i] = 0.0;
88 }
89 }
90
91 spec->b_ols = NULL;
92 spec->b_quad = NULL;
93 spec->b_cub = NULL;
94 spec->b_inv = NULL;
95 spec->b_log = NULL;
96 spec->b_linlog = NULL;
97
98 spec->code = PLOT_REGULAR;
99 spec->flags = 0;
100 spec->fit = PLOT_FIT_NONE;
101 spec->fp = NULL;
102 spec->data = NULL;
103 spec->auxdata = NULL;
104 spec->markers = NULL;
105 spec->n_markers = 0;
106 spec->scale = 1.0;
107 spec->labeled = NULL;
108 spec->ptr = NULL;
109 spec->bars = NULL;
110 spec->fontstr = NULL;
111 spec->reglist = NULL;
112 spec->nobs = 0;
113 spec->okobs = 0;
114 spec->pd = 0;
115 spec->nbars = 0;
116 spec->boxwidth = 0;
117 spec->fillfrac = 0;
118 spec->samples = 0;
119 spec->border = GP_BORDER_DEFAULT;
120 spec->border_lc[0] = '\0';
121 spec->bmargin = 0;
122
123 spec->termtype = GP_TERM_NONE;
124
125 return spec;
126 }
127
plotbars_free(plotbars * bars)128 static void plotbars_free (plotbars *bars)
129 {
130 if (bars != NULL) {
131 doubles_array_free(bars->dx, bars->n);
132 free(bars);
133 }
134 }
135
free_plotspec_lines(GPT_SPEC * spec)136 static void free_plotspec_lines (GPT_SPEC *spec)
137 {
138 int i;
139
140 for (i=0; i<spec->n_lines; i++) {
141 free(spec->lines[i].ustr);
142 free(spec->lines[i].mcols);
143 }
144
145 free(spec->lines);
146 }
147
plotspec_destroy(GPT_SPEC * spec)148 void plotspec_destroy (GPT_SPEC *spec)
149 {
150 if (spec == NULL) {
151 return;
152 }
153
154 if (spec->lines != NULL) {
155 free_plotspec_lines(spec);
156 }
157
158 if (spec->labels != NULL) {
159 free(spec->labels);
160 }
161
162 if (spec->arrows != NULL) {
163 free(spec->arrows);
164 }
165
166 if (spec->data != NULL) {
167 gretl_matrix_free(spec->data);
168 }
169
170 if (spec->auxdata != NULL) {
171 gretl_matrix_free(spec->auxdata);
172 }
173
174 if (spec->reglist != NULL) {
175 free(spec->reglist);
176 }
177
178 if (spec->literal != NULL) {
179 strings_array_free(spec->literal, spec->n_literal);
180 }
181
182 if (spec->markers != NULL) {
183 strings_array_free(spec->markers, spec->n_markers);
184 }
185
186 if (spec->labeled != NULL) {
187 free(spec->labeled);
188 }
189
190 if (spec->bars != NULL) {
191 plotbars_free(spec->bars);
192 }
193
194 if (spec->fontstr != NULL) {
195 free(spec->fontstr);
196 }
197
198 if (spec->xticstr != NULL) {
199 free(spec->xticstr);
200 }
201
202 if (spec->x2ticstr != NULL) {
203 free(spec->x2ticstr);
204 }
205
206 gretl_matrix_free(spec->b_ols);
207 gretl_matrix_free(spec->b_quad);
208 gretl_matrix_free(spec->b_cub);
209 gretl_matrix_free(spec->b_inv);
210 gretl_matrix_free(spec->b_log);
211 gretl_matrix_free(spec->b_linlog);
212
213 free(spec);
214 }
215
plotspec_get_xt1_xt2(const GPT_SPEC * spec,double * xt1,double * xt2)216 static void plotspec_get_xt1_xt2 (const GPT_SPEC *spec,
217 double *xt1,
218 double *xt2)
219 {
220 if (spec->data != NULL) {
221 *xt1 = spec->data->val[0];
222 *xt2 = spec->data->val[spec->nobs - 1];
223 }
224 }
225
226 static gp_style_spec style_specs[] = {
227 { GP_STYLE_LINES, "lines", N_("lines") },
228 { GP_STYLE_POINTS, "points", N_("points") },
229 { GP_STYLE_LINESPOINTS, "linespoints", N_("lines/points") },
230 { GP_STYLE_IMPULSES, "impulses", N_("impulses") },
231 { GP_STYLE_DOTS, "dots", N_("dots") },
232 { GP_STYLE_STEPS, "steps", N_("steps") },
233 { GP_STYLE_BOXES, "boxes", N_("boxes") },
234 { GP_STYLE_ERRORBARS, "errorbars", N_("error bars") },
235 { GP_STYLE_FILLEDCURVE, "filledcurve", N_("filled curve") },
236 { GP_STYLE_CANDLESTICKS, "candlesticks", N_("candlesticks") },
237 { 0, NULL, NULL }
238 };
239
gp_line_style_display_name(int t)240 const char *gp_line_style_display_name (int t)
241 {
242 int i;
243
244 for (i=0; style_specs[i].id != 0; i++) {
245 if (t == style_specs[i].id) {
246 return style_specs[i].trname;
247 }
248 }
249
250 return N_("lines");
251 }
252
gp_line_style_name(int t)253 static const char *gp_line_style_name (int t)
254 {
255 int i;
256
257 for (i=0; style_specs[i].id != 0; i++) {
258 if (t == style_specs[i].id) {
259 return style_specs[i].name;
260 }
261 }
262
263 return "lines";
264 }
265
gp_style_index_from_name(const char * s)266 int gp_style_index_from_name (const char *s)
267 {
268 int i;
269
270 for (i=0; style_specs[i].id != 0; i++) {
271 if (!strcmp(s, style_specs[i].name)) {
272 return style_specs[i].id;
273 }
274 }
275
276 /* allowed abbreviations */
277
278 if (!strcmp(s, "l")) {
279 return GP_STYLE_LINES;
280 } else if (!strcmp(s, "p")) {
281 return GP_STYLE_POINTS;
282 } else if (!strcmp(s, "lp")) {
283 return GP_STYLE_LINESPOINTS;
284 } else if (!strcmp(s, "i")) {
285 return GP_STYLE_IMPULSES;
286 } else if (!strcmp(s, "b")) {
287 return GP_STYLE_BOXES;
288 } else if (!strncmp(s, "can", 3)) {
289 return GP_STYLE_CANDLESTICKS;
290 }
291
292 /* fallback */
293 return GP_STYLE_AUTO;
294 }
295
gp_style_index_from_display_name(const char * s)296 int gp_style_index_from_display_name (const char *s)
297 {
298 int i;
299
300 for (i=0; style_specs[i].id != 0; i++) {
301 if (!strcmp(s, _(style_specs[i].trname))) {
302 return style_specs[i].id;
303 }
304 }
305
306 return GP_STYLE_LINES;
307 }
308
get_style_spec(int t)309 gp_style_spec *get_style_spec (int t)
310 {
311 int i;
312
313 for (i=0; style_specs[i].id != 0; i++) {
314 if (t == style_specs[i].id) {
315 return &style_specs[i];
316 }
317 }
318
319 return NULL;
320 }
321
322 static gp_key_spec key_specs[] = {
323 { GP_KEY_LEFT_TOP, N_("left top") },
324 { GP_KEY_RIGHT_TOP, N_("right top") },
325 { GP_KEY_LEFT_BOTTOM, N_("left bottom") },
326 { GP_KEY_RIGHT_BOTTOM, N_("right bottom") },
327 { GP_KEY_OUTSIDE, N_("outside") },
328 { GP_KEY_NONE, N_("none") },
329 /* in reverse order */
330 { GP_KEY_LEFT_TOP, N_("top left") },
331 { GP_KEY_RIGHT_TOP, N_("top right") },
332 { GP_KEY_LEFT_BOTTOM, N_("bottom left") },
333 { GP_KEY_RIGHT_BOTTOM, N_("bottom right") },
334 { -1, NULL }
335 };
336
gp_keypos_string(int t)337 static const char *gp_keypos_string (int t)
338 {
339 int i;
340
341 for (i=0; key_specs[i].str != NULL; i++) {
342 if (t == key_specs[i].id) {
343 return key_specs[i].str;
344 }
345 }
346
347 return N_("none");
348 }
349
print_keypos_string(int t,FILE * fp)350 void print_keypos_string (int t, FILE *fp)
351 {
352 const char *s = gp_keypos_string(t);
353
354 if (!strcmp(s, "none")) {
355 fputs("set nokey\n", fp);
356 } else {
357 fprintf(fp, "set key %s\n", s);
358 }
359 }
360
gp_keypos_from_name(const char * s)361 int gp_keypos_from_name (const char *s)
362 {
363 int i;
364
365 for (i=0; key_specs[i].id >= 0; i++) {
366 if (!strcmp(s, key_specs[i].str)) {
367 return key_specs[i].id;
368 }
369 }
370
371 return GP_KEY_NONE;
372 }
373
gp_keypos_from_display_name(const char * s)374 int gp_keypos_from_display_name (const char *s)
375 {
376 int i;
377
378 for (i=0; key_specs[i].id >= 0; i++) {
379 if (!strcmp(s, _(key_specs[i].str))) {
380 return key_specs[i].id;
381 }
382 }
383
384 return GP_KEY_NONE;
385 }
386
get_keypos_spec(int t)387 gp_key_spec *get_keypos_spec (int t)
388 {
389 int i;
390
391 for (i=0; key_specs[i].id >= 0; i++) {
392 if (t == key_specs[i].id) {
393 return &key_specs[i];
394 }
395 }
396
397 return NULL;
398 }
399
plotspec_add_line(GPT_SPEC * spec)400 int plotspec_add_line (GPT_SPEC *spec)
401 {
402 GPT_LINE *lines;
403 int n = spec->n_lines;
404
405 lines = realloc(spec->lines, (n + 1) * sizeof *lines);
406 if (lines == NULL) {
407 return E_ALLOC;
408 }
409
410 spec->lines = lines;
411 spec->n_lines += 1;
412
413 lines[n].varnum = 0;
414 lines[n].style = 0;
415 lines[n].pscale = 1.0;
416 lines[n].rgb[0] = '\0';
417 lines[n].yaxis = 1;
418 lines[n].type = LT_AUTO;
419 lines[n].ptype = 0;
420 lines[n].dtype = 0;
421 lines[n].width = 1.0;
422 lines[n].ncols = 0;
423 lines[n].whiskwidth = 0;
424 lines[n].flags = 0;
425
426 lines[n].title = NULL;
427 lines[n].formula = NULL;
428 lines[n].ustr = NULL;
429 lines[n].mcols = NULL;
430
431 return 0;
432 }
433
copy_line_content(GPT_LINE * targ,GPT_LINE * src)434 static void copy_line_content (GPT_LINE *targ, GPT_LINE *src)
435 {
436 targ->varnum = src->varnum;
437 targ->style = src->style;
438 targ->pscale = src->pscale;
439 strcpy(targ->rgb, src->rgb);
440 targ->yaxis = src->yaxis;
441 targ->type = src->type;
442 targ->ptype = src->ptype;
443 targ->dtype = src->dtype;
444 targ->width = src->width;
445 targ->ncols = src->ncols;
446 targ->whiskwidth = src->whiskwidth;
447 targ->flags = src->flags;
448
449 if (src->title != NULL) {
450 g_free(targ->title);
451 targ->title = g_strdup(src->title);
452 }
453 if (src->formula != NULL) {
454 g_free(targ->formula);
455 targ->formula = g_strdup(src->formula);
456 }
457 if (src->ustr != NULL) {
458 free(targ->ustr);
459 targ->ustr = gretl_strdup(src->ustr);
460 }
461 if (src->mcols != NULL) {
462 free(targ->mcols);
463 targ->mcols = gretl_list_copy(src->mcols);
464 }
465 }
466
plotspec_delete_line(GPT_SPEC * spec,int i)467 int plotspec_delete_line (GPT_SPEC *spec, int i)
468 {
469 GPT_LINE *lines = spec->lines;
470 int j, n = spec->n_lines;
471
472 if (i < 0 || i >= n) {
473 return E_DATA;
474 }
475
476 g_free(lines[i].title);
477 g_free(lines[i].formula);
478 free(lines[i].ustr);
479 free(lines[i].mcols);
480
481 /* move the remaining line contents down */
482 for (j=i; j<n-1; j++) {
483 copy_line_content(&lines[j], &lines[j+1]);
484 }
485
486 spec->n_lines -= 1;
487
488 lines = realloc(spec->lines, (n - 1) * sizeof *lines);
489 if (lines == NULL) {
490 return E_ALLOC;
491 }
492
493 spec->lines = lines;
494
495 return 0;
496 }
497
plotspec_clone_lines(GPT_SPEC * spec,int * err)498 GPT_LINE *plotspec_clone_lines (GPT_SPEC *spec, int *err)
499 {
500 GPT_LINE *lines = NULL;
501 int i;
502
503 if (spec->n_lines == 0) {
504 /* no-op */
505 return NULL;
506 }
507
508 lines = calloc(spec->n_lines, sizeof *lines);
509
510 if (lines == NULL) {
511 *err = E_ALLOC;
512 } else {
513 for (i=0; i<spec->n_lines; i++) {
514 lines[i].ustr = NULL;
515 lines[i].mcols = NULL;
516 copy_line_content(&lines[i], &spec->lines[i]);
517 }
518 }
519
520 return lines;
521 }
522
plotspec_max_line_width(GPT_SPEC * spec)523 int plotspec_max_line_width (GPT_SPEC *spec)
524 {
525 int i, lw = 0;
526
527 for (i=0; i<spec->n_lines; i++) {
528 if (spec->lines[i].width > lw) {
529 lw = spec->lines[i].width;
530 }
531 }
532
533 return lw;
534 }
535
plotspec_add_label(GPT_SPEC * spec)536 int plotspec_add_label (GPT_SPEC *spec)
537 {
538 GPT_LABEL *labels;
539 int n = spec->n_labels;
540
541 labels = realloc(spec->labels, (n + 1) * sizeof *labels);
542 if (labels == NULL) {
543 return E_ALLOC;
544 }
545
546 spec->labels = labels;
547 spec->n_labels += 1;
548
549 labels[n].text[0] = '\0';
550 labels[n].pos[0] = NADBL;
551 labels[n].pos[1] = NADBL;
552 labels[n].just = GP_JUST_LEFT;
553
554 return 0;
555 }
556
copy_label_content(GPT_LABEL * targ,GPT_LABEL * src)557 static void copy_label_content (GPT_LABEL *targ, GPT_LABEL *src)
558 {
559 strcpy(targ->text, src->text);
560 targ->pos[0] = src->pos[0];
561 targ->pos[1] = src->pos[1];
562 targ->just = src->just;
563 }
564
plotspec_delete_label(GPT_SPEC * spec,int i)565 int plotspec_delete_label (GPT_SPEC *spec, int i)
566 {
567 GPT_LABEL *labels = spec->labels;
568 int j, n = spec->n_labels;
569 int err = 0;
570
571 if (i < 0 || i >= n) {
572 return E_DATA;
573 }
574
575 for (j=i; j<n-1; j++) {
576 copy_label_content(&labels[j], &labels[j+1]);
577 }
578
579 spec->n_labels -= 1;
580
581 if (spec->n_labels == 0) {
582 free(spec->labels);
583 spec->labels = NULL;
584 } else {
585 labels = realloc(spec->labels, (n - 1) * sizeof *labels);
586 if (labels == NULL) {
587 err = E_ALLOC;
588 } else {
589 spec->labels = labels;
590 }
591 }
592
593 return err;
594 }
595
plotspec_clone_labels(GPT_SPEC * spec,int * err)596 GPT_LABEL *plotspec_clone_labels (GPT_SPEC *spec, int *err)
597 {
598 GPT_LABEL *labels = NULL;
599 int i;
600
601 if (spec->n_labels == 0) {
602 /* no-op */
603 return NULL;
604 }
605
606 labels = malloc(spec->n_labels * sizeof *labels);
607 if (labels == NULL) {
608 *err = E_ALLOC;
609 } else {
610 for (i=0; i<spec->n_labels; i++) {
611 copy_label_content(&labels[i], &spec->labels[i]);
612 }
613 }
614
615 return labels;
616 }
617
plotspec_add_arrow(GPT_SPEC * spec)618 int plotspec_add_arrow (GPT_SPEC *spec)
619 {
620 GPT_ARROW *arrows;
621 int n = spec->n_arrows;
622
623 arrows = realloc(spec->arrows, (n + 1) * sizeof *arrows);
624 if (arrows == NULL) {
625 return E_ALLOC;
626 }
627
628 spec->arrows = arrows;
629 spec->n_arrows += 1;
630 arrows[n].x0 = 0;
631 arrows[n].y0 = 0;
632 arrows[n].x1 = 0;
633 arrows[n].y1 = 0;
634 arrows[n].flags = 0;
635
636 return 0;
637 }
638
copy_arrow_content(GPT_ARROW * targ,GPT_ARROW * src)639 static void copy_arrow_content (GPT_ARROW *targ, GPT_ARROW *src)
640 {
641 targ->x0 = src->x0;
642 targ->y0 = src->y0;
643 targ->x1 = src->x1;
644 targ->y1 = src->y1;
645 targ->flags = src->flags;
646 }
647
plotspec_delete_arrow(GPT_SPEC * spec,int i)648 int plotspec_delete_arrow (GPT_SPEC *spec, int i)
649 {
650 GPT_ARROW *arrows = spec->arrows;
651 int j, n = spec->n_arrows;
652 int err = 0;
653
654 if (i < 0 || i >= n) {
655 return E_DATA;
656 }
657
658 for (j=i; j<n-1; j++) {
659 copy_arrow_content(&arrows[j], &arrows[j+1]);
660 }
661
662 spec->n_arrows -= 1;
663
664 if (spec->n_arrows == 0) {
665 free(spec->arrows);
666 spec->arrows = NULL;
667 } else {
668 arrows = realloc(spec->arrows, (n - 1) * sizeof *arrows);
669 if (arrows == NULL) {
670 err = E_ALLOC;
671 } else {
672 spec->arrows = arrows;
673 }
674 }
675
676 return err;
677 }
678
plotspec_clone_arrows(GPT_SPEC * spec,int * err)679 GPT_ARROW *plotspec_clone_arrows (GPT_SPEC *spec, int *err)
680 {
681 GPT_ARROW *arrows = NULL;
682 int i;
683
684 if (spec->n_arrows == 0) {
685 /* no-op */
686 return NULL;
687 }
688
689 arrows = malloc(spec->n_arrows * sizeof *arrows);
690 if (arrows == NULL) {
691 *err = E_ALLOC;
692 } else {
693 for (i=0; i<spec->n_arrows; i++) {
694 copy_arrow_content(&arrows[i], &spec->arrows[i]);
695 }
696 }
697
698 return arrows;
699 }
700
escape_quotes(const char * s)701 static char *escape_quotes (const char *s)
702 {
703 if (strchr(s, '"') == NULL) {
704 return NULL;
705 } else {
706 int qcount = 0;
707 char *ret, *r;
708 const char *p = s;
709
710 while (*p) {
711 if (*p == '"') qcount++;
712 p++;
713 }
714
715 ret = malloc(strlen(s) + 1 + qcount);
716 if (ret == NULL) {
717 return NULL;
718 }
719
720 r = ret;
721 while (*s) {
722 if (*s == '"') {
723 *r++ = '\\';
724 *r++ = '"';
725 } else {
726 *r++ = *s;
727 }
728 s++;
729 }
730 *r = 0;
731
732 return ret;
733 }
734 }
735
gp_justification_string(int j)736 const char *gp_justification_string (int j)
737 {
738 if (j == GP_JUST_LEFT) {
739 return N_("left");
740 } else if (j == GP_JUST_CENTER) {
741 return N_("center");
742 } else if (j == GP_JUST_RIGHT) {
743 return N_("right");
744 } else {
745 return N_("left");
746 }
747 }
748
print_plot_labelspec(const GPT_LABEL * lbl,FILE * fp)749 static void print_plot_labelspec (const GPT_LABEL *lbl, FILE *fp)
750 {
751 char *label = escape_quotes(lbl->text);
752
753 fprintf(fp, "set label \"%s\" ", (label != NULL)?
754 label : lbl->text);
755
756 gretl_push_c_numeric_locale();
757
758 fprintf(fp, "at %g,%g %s front\n",
759 lbl->pos[0], lbl->pos[1],
760 gp_justification_string(lbl->just));
761
762 gretl_pop_c_numeric_locale();
763
764 if (label != NULL) {
765 free(label);
766 }
767 }
768
print_plot_arrow(const GPT_ARROW * arrow,FILE * fp)769 static void print_plot_arrow (const GPT_ARROW *arrow, FILE *fp)
770 {
771 gretl_push_c_numeric_locale();
772 fprintf(fp, "set arrow from %g,%g to %g,%g %s",
773 arrow->x0, arrow->y0, arrow->x1, arrow->y1,
774 (arrow->flags & GP_ARROW_HEAD)? "head" : "nohead");
775 if (arrow->flags & GP_ARROW_DOTS) {
776 fputs(" lt 0", fp);
777 }
778 fputc('\n', fp);
779 gretl_pop_c_numeric_locale();
780 }
781
print_polar_labels(const GPT_SPEC * spec,FILE * fp)782 static int print_polar_labels (const GPT_SPEC *spec, FILE *fp)
783 {
784 double x, y;
785 int t;
786
787 fputs("# printing data labels\n", fp);
788
789 gretl_push_c_numeric_locale();
790
791 for (t=0; t<spec->nobs; t++) {
792 if (spec->labeled != NULL && !spec->labeled[t]) {
793 /* printing only specified labels */
794 continue;
795 }
796 if (sscanf(spec->markers[t], "%lf,%lf", &x, &y) == 2) {
797 fprintf(fp, "set label \"%.3f,%.3f\" at %.3f,%.3f\n",
798 x, y, x, y + .04);
799 }
800 }
801
802 gretl_pop_c_numeric_locale();
803
804 return 0;
805 }
806
usable_obs(const double * x,const double * y0,const double * y1,int t,const double ** py)807 static int usable_obs (const double *x, const double *y0,
808 const double *y1, int t,
809 const double **py)
810 {
811 *py = y0;
812
813 if (!na(x[t]) && !na(y0[t])) {
814 return 1;
815 } else if (!na(x[t]) && y1 != NULL && !na(y1[t])) {
816 *py = y1;
817 return 1;
818 } else {
819 return 0;
820 }
821 }
822
plotspec_line_is_formula(const GPT_SPEC * spec,int i)823 int plotspec_line_is_formula (const GPT_SPEC *spec, int i)
824 {
825 int ret = 0;
826
827 if (i >= 0 && i < spec->n_lines) {
828 GPT_LINE *line = &spec->lines[i];
829
830 if (!string_is_blank(line->formula)) {
831 ret = 1;
832 } else if (line->flags & GP_LINE_USER) {
833 ret = 1;
834 } else if (i == 1 && (spec->flags & GPT_AUTO_FIT)) {
835 ret = 1;
836 }
837 }
838
839 return ret;
840 }
841
print_data_labels(const GPT_SPEC * spec,FILE * fp)842 static int print_data_labels (const GPT_SPEC *spec, FILE *fp)
843 {
844 const double *x, *y, *y0;
845 const double *y1 = NULL;
846 double xrange, yrange;
847 double xmin, xmax;
848 double ymin, ymax;
849 double yoff;
850 int nlines;
851 int i, t;
852
853 if (spec->markers == NULL) {
854 /* "can't happen" */
855 return 1;
856 }
857
858 if (spec->flags & GPT_POLAR) {
859 return print_polar_labels(spec, fp);
860 }
861
862 nlines = 0;
863 for (i=0; i<spec->n_lines; i++) {
864 if (!plotspec_line_is_formula(spec, i)) {
865 nlines++;
866 }
867 }
868 if (nlines > 2 || spec->lines[0].ncols != 2) {
869 return 1;
870 }
871
872 x = spec->data->val;
873 y0 = x + spec->nobs;
874
875 if (spec->code == PLOT_FACTORIZED) {
876 y1 = y0 + spec->nobs;
877 }
878
879 xmin = spec->range[GP_X_RANGE][0];
880 xmax = spec->range[GP_X_RANGE][1];
881 xrange = (na(xmin) || na(xmax)) ? 0.0 : xmax - xmin;
882
883 ymin = spec->range[GP_Y_RANGE][0];
884 ymax = spec->range[GP_Y_RANGE][1];
885 yrange = (na(ymin) || na(ymax)) ? 0.0 : ymax - ymin;
886
887 if (xrange == 0.0 || yrange == 0.0) {
888 ymin = 1.0e+50; ymax = -1.0e+50;
889 xmin = 1.0e+50; xmax = -1.0e+50;
890
891 for (t=0; t<spec->nobs; t++) {
892 if (usable_obs(x, y0, y1, t, &y)) {
893 if (yrange == 0.0) {
894 if (y[t] < ymin) {
895 ymin = y[t];
896 }
897 if (y[t] > ymax) {
898 ymax = y[t];
899 }
900 }
901 if (xrange == 0.0) {
902 if (x[t] < xmin) {
903 xmin = x[t];
904 }
905 if (x[t] > xmax) {
906 xmax = x[t];
907 }
908 }
909 }
910 }
911 if (yrange == 0.0) {
912 yrange = ymax - ymin;
913 }
914 if (xrange == 0.0) {
915 xrange = xmax - xmin;
916 }
917 }
918
919 yoff = 0.03 * yrange;
920
921 fputs("# printing data labels\n", fp);
922
923 gretl_push_c_numeric_locale();
924
925 for (t=0; t<spec->nobs; t++) {
926 double xoff = 0.0;
927
928 if (spec->labeled != NULL && !spec->labeled[t]) {
929 /* printing only specified labels */
930 continue;
931 }
932
933 if (usable_obs(x, y0, y1, t, &y)) {
934 if (x[t] > .90 * xrange) {
935 xoff = -.02 * xrange;
936 }
937 fprintf(fp, "set label \"%s\" at %.8g,%.8g\n", spec->markers[t],
938 x[t] + xoff, y[t] + yoff);
939 }
940 }
941
942 gretl_pop_c_numeric_locale();
943
944 return 0;
945 }
946
print_auto_fit_string(FitType fit,FILE * fp)947 void print_auto_fit_string (FitType fit, FILE *fp)
948 {
949 if (fit == PLOT_FIT_OLS) {
950 fputs("# plot includes automatic fit: OLS\n", fp);
951 } else if (fit == PLOT_FIT_QUADRATIC) {
952 fputs("# plot includes automatic fit: quadratic\n", fp);
953 } else if (fit == PLOT_FIT_CUBIC) {
954 fputs("# plot includes automatic fit: cubic\n", fp);
955 } else if (fit == PLOT_FIT_INVERSE) {
956 fputs("# plot includes automatic fit: inverse\n", fp);
957 } else if (fit == PLOT_FIT_LOESS) {
958 fputs("# plot includes automatic fit: loess\n", fp);
959 } else if (fit == PLOT_FIT_LOGLIN) {
960 fputs("# plot includes automatic fit: semilog\n", fp);
961 } else if (fit == PLOT_FIT_LINLOG) {
962 fputs("# plot includes automatic fit: linlog\n", fp);
963 }
964 }
965
print_plot_ranges_etc(const GPT_SPEC * spec,FILE * fp)966 void print_plot_ranges_etc (const GPT_SPEC *spec, FILE *fp)
967 {
968 const char *rstrs[] = {
969 "x", "y", "y2", "t", "x2"
970 };
971 int i;
972
973 gretl_push_c_numeric_locale();
974
975 for (i=0; i<5; i++) {
976 if (i < 3 && spec->logbase[i] > 0.0) {
977 fprintf(fp, "set logscale %s %g\n", rstrs[i], spec->logbase[i]);
978 }
979
980 if (na(spec->range[i][0]) || na(spec->range[i][1]) ||
981 spec->range[i][0] == spec->range[i][1]) {
982 continue;
983 }
984
985 if ((i == GP_Y2_RANGE && !(spec->flags & GPT_Y2AXIS)) ||
986 (i == GP_T_RANGE && !(spec->flags & GPT_PARAMETRIC))) {
987 continue;
988 }
989
990 fprintf(fp, "set %srange [%.10g:%.10g]\n", rstrs[i],
991 spec->range[i][0], spec->range[i][1]);
992
993 if (i == 4 && spec->code == PLOT_PROB_DIST && spec->samples == 0) {
994 int ns = spec->range[i][1] - spec->range[i][0] + 1;
995
996 fprintf(fp, "set samples %d\n", ns);
997 }
998 }
999
1000 if (spec->boxwidth > 0 && spec->boxwidth < 1) {
1001 fprintf(fp, "set boxwidth %g relative\n", (double) spec->boxwidth);
1002 } else if (spec->boxwidth < 0 && spec->boxwidth > -1) {
1003 fprintf(fp, "set boxwidth %g absolute\n", (double) -spec->boxwidth);
1004 }
1005
1006 gretl_pop_c_numeric_locale();
1007 }
1008
blank_user_line(GPT_LINE * line)1009 static int blank_user_line (GPT_LINE *line)
1010 {
1011 return ((line->flags & GP_LINE_USER) &&
1012 string_is_blank(line->formula));
1013 }
1014
print_user_lines_info(const GPT_SPEC * spec,FILE * fp)1015 static void print_user_lines_info (const GPT_SPEC *spec, FILE *fp)
1016 {
1017 int i, n = 0;
1018
1019 for (i=0; i<spec->n_lines; i++) {
1020 if (spec->lines[i].flags & GP_LINE_USER) {
1021 n++;
1022 }
1023 }
1024
1025 if (n > 0) {
1026 fprintf(fp, "# %d user-defined lines: ", n);
1027 for (i=0; i<spec->n_lines; i++) {
1028 if (spec->lines[i].flags & GP_LINE_USER) {
1029 fprintf(fp, "%d ", i);
1030 }
1031 }
1032 fputc('\n', fp);
1033 }
1034 }
1035
more_lines(const GPT_SPEC * spec,int i,int skipline)1036 static int more_lines (const GPT_SPEC *spec, int i, int skipline)
1037 {
1038 if (i == spec->n_lines - 1) {
1039 /* at last line */
1040 return 0;
1041 } else {
1042 int j;
1043
1044 for (j=i+1; j<spec->n_lines; j++) {
1045 if (j != skipline && !blank_user_line(&spec->lines[j])) {
1046 return 1;
1047 }
1048 }
1049 return 0;
1050 }
1051 }
1052
print_linestyle(const GPT_SPEC * spec,int targ,int src,FILE * fp)1053 static void print_linestyle (const GPT_SPEC *spec,
1054 int targ, int src,
1055 FILE *fp)
1056 {
1057 GPT_LINE *line;
1058 int done = 0;
1059
1060 fprintf(fp, "set linetype %d ", targ + 1);
1061
1062 if (src < spec->n_lines) {
1063 line = &spec->lines[src];
1064 if (*line->rgb != '\0' && line->style != GP_STYLE_FILLEDCURVE) {
1065 fprintf(fp, "lc rgb \"%s\"\n", line->rgb);
1066 done = 1;
1067 }
1068 }
1069
1070 if (!done) {
1071 /* hmm, should we be doing this? */
1072 gretlRGB color = get_graph_color(targ);
1073 char cstr[8];
1074
1075 print_rgb_hash(cstr, color);
1076 fprintf(fp, "lc rgb \"%s\"\n", cstr);
1077 }
1078 }
1079
write_styles_from_plotspec(const GPT_SPEC * spec,FILE * fp)1080 static void write_styles_from_plotspec (const GPT_SPEC *spec, FILE *fp)
1081 {
1082 char cstr[8];
1083 int i;
1084
1085 if (frequency_plot_code(spec->code)) {
1086 gretlRGB color = get_boxcolor();
1087
1088 print_rgb_hash(cstr, color);
1089 fprintf(fp, "set linetype 1 lc rgb \"%s\"\n", cstr);
1090 fputs("set linetype 2 lc rgb \"#000000\"\n", fp);
1091 } else if (spec->code == PLOT_RQ_TAU) {
1092 fputs("set linetype 1 lc rgb \"#000000\"\n", fp);
1093 for (i=1; i<N_GP_LINETYPES; i++) {
1094 print_linestyle(spec, i, i, fp);
1095 }
1096 } else if (spec->code == PLOT_BOXPLOTS) {
1097 /* the first two elements use the same color */
1098 print_linestyle(spec, 0, 0, fp);
1099 print_linestyle(spec, 1, 2, fp);
1100 } else {
1101 /* note: handle the case where "line 0" is filledcurve,
1102 the color of which is handled separately
1103 */
1104 int offset = 0;
1105
1106 if (spec->lines[0].style == GP_STYLE_FILLEDCURVE) {
1107 offset = 1;
1108 }
1109 for (i=0; i<N_GP_LINETYPES; i++) {
1110 print_linestyle(spec, i, i + offset, fp);
1111 }
1112 }
1113 }
1114
maybe_print_point_info(const GPT_LINE * line,FILE * fp)1115 static void maybe_print_point_info (const GPT_LINE *line, FILE *fp)
1116 {
1117 if (line->style == GP_STYLE_POINTS ||
1118 line->style == GP_STYLE_LINESPOINTS) {
1119 if (line->ptype != 0) {
1120 fprintf(fp, " pt %d", line->ptype);
1121 }
1122 if (line->pscale != 1.0) {
1123 fprintf(fp, " ps %g", (double) line->pscale);
1124 }
1125 }
1126
1127 if (line->style == GP_STYLE_LINES ||
1128 line->style == GP_STYLE_LINESPOINTS) {
1129 if (line->dtype > 1) {
1130 fprintf(fp, " dt %d", line->dtype);
1131 }
1132 }
1133 }
1134
1135 #define show_fit(s) (s->fit == PLOT_FIT_OLS || \
1136 s->fit == PLOT_FIT_QUADRATIC || \
1137 s->fit == PLOT_FIT_CUBIC || \
1138 s->fit == PLOT_FIT_INVERSE || \
1139 s->fit == PLOT_FIT_LOESS || \
1140 s->fit == PLOT_FIT_LOGLIN || \
1141 s->fit == PLOT_FIT_LINLOG)
1142
gp_line_data_columns(GPT_SPEC * spec,int i)1143 int gp_line_data_columns (GPT_SPEC *spec, int i)
1144 {
1145 if (spec->lines[i].flags & GP_LINE_AUXDATA) {
1146 return 0;
1147 } else {
1148 return spec->lines[i].ncols;
1149 }
1150 }
1151
plotspec_print_data(GPT_SPEC * spec,int skipline,int * miss,FILE * fp)1152 static void plotspec_print_data (GPT_SPEC *spec,
1153 int skipline,
1154 int *miss,
1155 FILE *fp)
1156 {
1157 double *x[5];
1158 int started_data_lines = 0;
1159 int i, j, t;
1160
1161 for (i=0; i<spec->n_lines; i++) {
1162 int ncols = gp_line_data_columns(spec, i);
1163
1164 if (ncols == 0 || i == skipline) {
1165 /* no (regular) data to print */
1166 continue;
1167 }
1168
1169 if (!started_data_lines) {
1170 x[0] = spec->data->val;
1171 /* see below for subsequent adjustment of x[1] */
1172 x[1] = x[0] + spec->nobs;
1173 started_data_lines = 1;
1174 }
1175
1176 x[2] = x[1] + spec->nobs;
1177 x[3] = x[2] + spec->nobs;
1178 x[4] = x[3] + spec->nobs;
1179
1180 for (t=0; t<spec->nobs; t++) {
1181 /* print x-axis value */
1182 if (na(x[0][t])) {
1183 write_gp_dataval(NADBL, fp, 0);
1184 *miss = 1;
1185 } else if (spec->flags & GPT_TIMEFMT) {
1186 fprintf(fp, "%.0f ", x[0][t]);
1187 } else {
1188 fprintf(fp, "%.10g ", x[0][t]);
1189 }
1190 /* print y-axis value(s) */
1191 for (j=1; j<ncols; j++) {
1192 if (na(x[j][t])) {
1193 *miss = 1;
1194 }
1195 write_gp_dataval(x[j][t], fp, 0);
1196 }
1197 /* append markers if applicable */
1198 if (spec->markers != NULL && i == 0) {
1199 fprintf(fp, " # %s", spec->markers[t]);
1200 }
1201 fputc('\n', fp);
1202 }
1203
1204 fputs("e\n", fp);
1205
1206 x[1] += (ncols - 1) * spec->nobs;
1207 }
1208
1209 if (spec->auxdata != NULL) {
1210 int rows = gretl_matrix_rows(spec->auxdata);
1211 int cols = gretl_matrix_cols(spec->auxdata);
1212
1213 fprintf(fp, "# auxdata %d %d\n", rows, cols);
1214
1215 for (i=0; i<rows; i++) {
1216 for (j=0; j<cols; j++) {
1217 fprintf(fp, "%.10g ", gretl_matrix_get(spec->auxdata, i, j));
1218 }
1219 fputc('\n', fp);
1220 }
1221 fputs("e\n", fp);
1222 }
1223 }
1224
plotspec_print_heredata(GPT_SPEC * spec,int * miss,FILE * fp)1225 static void plotspec_print_heredata (GPT_SPEC *spec,
1226 int *miss,
1227 FILE *fp)
1228 {
1229 gretl_matrix *m = NULL;
1230 double mti;
1231 int i, t;
1232
1233 if (spec->auxdata != NULL) {
1234 int r = spec->auxdata->rows;
1235 int c = spec->auxdata->cols;
1236 int j;
1237
1238 fprintf(fp, "# auxdata %d %d\n", r, c);
1239 fputs("$aux << EOA\n", fp);
1240
1241 for (i=0; i<r; i++) {
1242 for (j=0; j<c; j++) {
1243 fprintf(fp, "%.10g ", gretl_matrix_get(spec->auxdata, i, j));
1244 }
1245 fputc('\n', fp);
1246 }
1247 fputs("EOA\n", fp);
1248 }
1249
1250 if (spec->data == NULL || spec->datacols == 0) {
1251 return;
1252 }
1253
1254 m = spec->data;
1255
1256 fputs("$data << EOD\n", fp);
1257
1258 for (t=0; t<spec->nobs; t++) {
1259 for (i=0; i<m->cols; i++) {
1260 mti = gretl_matrix_get(m, t, i);
1261 if (na(mti)) {
1262 write_gp_dataval(NADBL, fp, 0);
1263 *miss = 1;
1264 } else if (i == 0 && (spec->flags & GPT_TIMEFMT)) {
1265 fprintf(fp, "%.0f ", mti);
1266 } else {
1267 fprintf(fp, "%.10g ", mti);
1268 }
1269 }
1270 if (spec->markers != NULL) {
1271 fprintf(fp, " # %s", spec->markers[t]);
1272 }
1273 fputc('\n', fp);
1274 }
1275
1276 fputs("EOD\n", fp);
1277 }
1278
plotspec_print(GPT_SPEC * spec,FILE * fp)1279 int plotspec_print (GPT_SPEC *spec, FILE *fp)
1280 {
1281 int png = get_png_output(spec);
1282 int mono = (spec->flags & GPT_MONO);
1283 double xt1 = 0, xt2 = 0;
1284 char src[16] = "'-'";
1285 int skipline = -1;
1286 int ycol, any_y2 = 0;
1287 int anydata = 0;
1288 int i, k, miss = 0;
1289
1290 if (spec->pd > 0) {
1291 fprintf(fp, "# timeseries %d", spec->pd);
1292 if (spec->flags & GPT_LETTERBOX) {
1293 fputs(" (letterbox)\n", fp);
1294 } else {
1295 fputc('\n', fp);
1296 }
1297 }
1298
1299 if (spec->scale != 1.0) {
1300 gretl_push_c_numeric_locale();
1301 fprintf(fp, "# scale = %.1f\n", spec->scale);
1302 gretl_pop_c_numeric_locale();
1303 }
1304
1305 if (mono) {
1306 fputs("set mono\n", fp);
1307 } else {
1308 write_styles_from_plotspec(spec, fp);
1309 }
1310
1311 if (!string_is_blank(spec->titles[0])) {
1312 fprintf(fp, "set title \"%s\"\n", spec->titles[0]);
1313 }
1314
1315 if (!string_is_blank(spec->titles[1])) {
1316 fprintf(fp, "set xlabel \"%s\"\n", spec->titles[1]);
1317 }
1318
1319 if (!string_is_blank(spec->titles[2])) {
1320 fprintf(fp, "set ylabel \"%s\"\n", spec->titles[2]);
1321 }
1322
1323 if ((spec->flags & GPT_Y2AXIS) && !string_is_blank(spec->titles[3])) {
1324 fprintf(fp, "set y2label \"%s\"\n", spec->titles[3]);
1325 }
1326
1327 if (!string_is_blank(spec->titles[4])) {
1328 fprintf(fp, "set x2label \"%s\"\n", spec->titles[4]);
1329 }
1330
1331 for (i=0; i<spec->n_labels; i++) {
1332 if (!string_is_blank(spec->labels[i].text)) {
1333 print_plot_labelspec(&spec->labels[i], fp);
1334 }
1335 }
1336
1337 for (i=0; i<spec->n_arrows; i++) {
1338 print_plot_arrow(&spec->arrows[i], fp);
1339 }
1340
1341 if (spec->flags & GPT_XZEROAXIS) {
1342 fputs("set xzeroaxis\n", fp);
1343 }
1344
1345 if (spec->flags & GPT_YZEROAXIS) {
1346 fputs("set yzeroaxis\n", fp);
1347 }
1348
1349 if (spec->flags & (GPT_GRID_Y | GPT_GRID_X)) {
1350 if (!(spec->flags & GPT_GRID_X)) {
1351 fputs("set grid ytics\n", fp);
1352 } else if (!(spec->flags & GPT_GRID_Y)) {
1353 fputs("set grid xtics\n", fp);
1354 } else {
1355 /* default, both */
1356 fputs("set grid\n", fp);
1357 }
1358 }
1359
1360 if (spec->flags & GPT_PARAMETRIC) {
1361 fputs("set parametric\n", fp);
1362 if (spec->samples > 0) {
1363 fprintf(fp, "set samples %d\n", spec->samples);
1364 }
1365 }
1366
1367 if (spec->flags & GPT_POLAR) {
1368 fputs("unset ytics\n", fp);
1369 fputs("set size square\n", fp);
1370 fputs("set polar\n", fp);
1371 }
1372
1373 gnuplot_missval_string(fp);
1374
1375 if (spec->keyspec == GP_KEY_NONE) {
1376 fputs("set nokey\n", fp);
1377 } else {
1378 fprintf(fp, "set key %s\n", gp_keypos_string(spec->keyspec));
1379 }
1380
1381 print_plot_ranges_etc(spec, fp);
1382
1383 /* using time format for x-axis? */
1384 if (*spec->timefmt != '\0') {
1385 fputs("set xdata time\n", fp);
1386 fprintf(fp, "set timefmt \"%s\"\n", spec->timefmt);
1387 }
1388
1389 /* special x and/or y format? */
1390 if (*spec->xfmt != '\0') {
1391 fprintf(fp, "set format x \"%s\"\n", spec->xfmt);
1392 }
1393 if (*spec->yfmt != '\0') {
1394 fprintf(fp, "set format y \"%s\"\n", spec->yfmt);
1395 }
1396
1397 /* customized xtics? */
1398 if (!strcmp(spec->xtics, "none")) {
1399 fputs("set noxtics\n", fp);
1400 } else {
1401 if (!string_is_blank(spec->xtics)) {
1402 /* may contain "nomirror" */
1403 fprintf(fp, "set xtics %s\n", spec->xtics);
1404 }
1405 if (spec->xticstr != NULL) {
1406 fprintf(fp, "set xtics %s\n", spec->xticstr);
1407 }
1408 if (!string_is_blank(spec->mxtics)) {
1409 fprintf(fp, "set mxtics %s\n", spec->mxtics);
1410 }
1411 }
1412
1413 /* using x2tics? */
1414 if (spec->x2ticstr != NULL) {
1415 fprintf(fp, "set x2tics %s\n", spec->x2ticstr);
1416 }
1417
1418 /* customized ytics? */
1419 if (!strcmp(spec->ytics, "none")) {
1420 fputs("set noytics\n", fp);
1421 } else {
1422 if (!string_is_blank(spec->ytics)) {
1423 fprintf(fp, "set ytics %s\n", spec->ytics);
1424 }
1425 }
1426
1427 if (spec->flags & GPT_Y2AXIS) {
1428 /* using two y axes */
1429 fputs("set ytics nomirror\n", fp);
1430 fputs("set y2tics\n", fp);
1431 } else if (spec->border != GP_BORDER_DEFAULT) {
1432 /* suppressing all or part of border */
1433 if (spec->border == 0) {
1434 fputs("unset border\n", fp);
1435 } else if (spec->border_lc[0] != '\0') {
1436 fprintf(fp, "set border %d lc rgb \"%s\"\n",
1437 spec->border, spec->border_lc);
1438 } else {
1439 fprintf(fp, "set border %d\n", spec->border);
1440 }
1441 if (string_is_blank(spec->xtics)) {
1442 fputs("set xtics nomirror\n", fp);
1443 }
1444 if (string_is_blank(spec->ytics)) {
1445 fputs("set ytics nomirror\n", fp);
1446 }
1447 }
1448
1449 if (spec->bmargin > 0) {
1450 fprintf(fp, "set bmargin %d\n", spec->bmargin);
1451 }
1452
1453 /* in case of plots that are editable (see gui client), it is
1454 important to write out the comment string that identifies the
1455 sort of graph, so that it will be recognized by type when
1456 it is redisplayed */
1457
1458 write_plot_type_string(spec->code, spec->flags, fp);
1459
1460 if (spec->n_literal > 0) {
1461 fprintf(fp, "# literal lines = %d\n", spec->n_literal);
1462 for (i=0; i<spec->n_literal; i++) {
1463 if (spec->literal[i] != NULL && *spec->literal[i] != '\0') {
1464 fprintf(fp, "%s\n", spec->literal[i]);
1465 } else {
1466 fputs("# empty line!\n", fp);
1467 }
1468 }
1469 }
1470
1471 if (show_fit(spec)) {
1472 print_auto_fit_string(spec->fit, fp);
1473 }
1474
1475 if (spec->bars != NULL) {
1476 plotspec_get_xt1_xt2(spec, &xt1, &xt2);
1477 spec->nbars = n_bars_shown(xt1, xt2, spec->bars);
1478 if (spec->nbars > 0) {
1479 fprintf(fp, "# n_bars = %d\n", spec->nbars);
1480 }
1481 }
1482
1483 print_user_lines_info(spec, fp);
1484
1485 if ((spec->code == PLOT_FREQ_SIMPLE ||
1486 spec->code == PLOT_FREQ_NORMAL ||
1487 spec->code == PLOT_FREQ_GAMMA)) {
1488 if (mono) {
1489 fputs("set style fill solid 0.3\n", fp);
1490 } else {
1491 fputs("set style fill solid 0.6\n", fp);
1492 }
1493 } else if (spec->fillfrac > 0) {
1494 gretl_push_c_numeric_locale();
1495 fprintf(fp, "set style fill solid %g\n", (double) spec->fillfrac);
1496 gretl_pop_c_numeric_locale();
1497 }
1498
1499 if (spec->flags & GPT_PRINT_MARKERS) {
1500 print_data_labels(spec, fp);
1501 }
1502
1503 if (spec->flags & GPT_FIT_HIDDEN) {
1504 skipline = 1;
1505 }
1506
1507 for (i=0; i<spec->n_lines; i++) {
1508 if (i == skipline || blank_user_line(&spec->lines[i])) {
1509 continue;
1510 }
1511 if ((spec->flags & GPT_Y2AXIS) && spec->lines[i].yaxis != 1) {
1512 any_y2 = 1;
1513 break;
1514 }
1515 }
1516
1517 if ((spec->bars != NULL && spec->nbars > 0) ||
1518 spec->data != NULL || spec->auxdata != NULL) {
1519 anydata = 1;
1520 }
1521
1522 if (spec->heredata && anydata) {
1523 fputs("# start inline data\n", fp);
1524 if (spec->bars != NULL && spec->nbars > 0) {
1525 print_plotbars(spec, fp);
1526 }
1527 if (spec->data != NULL || spec->auxdata != NULL) {
1528 plotspec_print_heredata(spec, &miss, fp);
1529 }
1530 fputs("# end inline data\n", fp);
1531 strcpy(src, "$data");
1532 }
1533
1534 fputs("plot \\\n", fp);
1535
1536 gretl_push_c_numeric_locale();
1537
1538 if (spec->nbars > 0) {
1539 print_bars_header(spec, fp);
1540 }
1541
1542 ycol = 2;
1543
1544 for (i=0; i<spec->n_lines; i++) {
1545 GPT_LINE *line = &spec->lines[i];
1546
1547 if (i == skipline || blank_user_line(line)) {
1548 if (i < spec->n_lines - 1) {
1549 continue;
1550 } else {
1551 break;
1552 }
1553 }
1554
1555 if (!string_is_blank(line->formula)) {
1556 fprintf(fp, "%s ", line->formula);
1557 } else if (line->ustr != NULL) {
1558 fprintf(fp, "%s using %s ", src, line->ustr);
1559 } else if (line->mcols != NULL) {
1560 int j;
1561
1562 fprintf(fp, "%s using ", src);
1563 for (j=1; j<=line->mcols[0]; j++) {
1564 fprintf(fp, "%d", line->mcols[j]);
1565 fputc(j < line->mcols[0] ? ':' : ' ', fp);
1566 }
1567 } else {
1568 fprintf(fp, "%s using 1", src);
1569 if (line->ncols == 5) {
1570 /* Note: boxplot candlesticks, hard-wired! */
1571 fprintf(fp, ":%d:%d:%d:%d", ycol+1, ycol, ycol+3, ycol+2);
1572 } else if (line->ncols == 2) {
1573 if (line->flags & GP_LINE_BOXDATA) {
1574 /* boxplot median, hard-wired */
1575 fprintf(fp, ":%d:%d:%d:%d", ycol, ycol, ycol, ycol);
1576 } else {
1577 fprintf(fp, ":%d", ycol);
1578 }
1579 } else {
1580 for (k=2; k<=line->ncols; k++) {
1581 fprintf(fp, ":%d", ycol + k - 2);
1582 }
1583 }
1584 fputc(' ', fp);
1585 }
1586
1587 if ((spec->flags & GPT_Y2AXIS) && line->yaxis != 1) {
1588 fprintf(fp, "axes x1y%d ", line->yaxis);
1589 }
1590
1591 if (string_is_blank(line->title)) {
1592 fputs("notitle ", fp);
1593 } else {
1594 fprintf(fp, "title \"%s", line->title);
1595 if (any_y2 && strchr(line->title, '(') == NULL) {
1596 if (line->yaxis == 1) {
1597 fprintf(fp, " (%s)\" ", _("left"));
1598 } else {
1599 fprintf(fp, " (%s)\" ", _("right"));
1600 }
1601 } else {
1602 fputs("\" ", fp);
1603 }
1604 }
1605
1606 if (line->style == GP_STYLE_FILLEDCURVE && line->type == LT_AUTO) {
1607 if (*line->rgb != '\0') {
1608 fprintf(fp, "lc rgb \"%s\" ", line->rgb);
1609 } else {
1610 print_filledcurve_color(fp);
1611 }
1612 }
1613
1614 if (line->style != GP_STYLE_AUTO) {
1615 fprintf(fp, "w %s", gp_line_style_name(line->style));
1616 }
1617
1618 if (line->type != LT_AUTO) {
1619 fprintf(fp, " lt %d", line->type);
1620 } else if (spec->nbars > 0 && line->style != GP_STYLE_FILLEDCURVE) {
1621 fprintf(fp, " lt %d", i + 1);
1622 }
1623
1624 maybe_print_point_info(line, fp);
1625
1626 if (line->style != GP_STYLE_FILLEDCURVE) {
1627 if (line->width == 1.0 && spec->scale > 1.0) {
1628 fprintf(fp, " lw 2");
1629 } else if (line->width != 1) {
1630 fprintf(fp, " lw %g", (double) line->width);
1631 }
1632 }
1633
1634 if (line->whiskwidth > 0) {
1635 fprintf(fp, " whiskerbars %g", (double) line->whiskwidth);
1636 }
1637
1638 if (spec->heredata) {
1639 ycol += line->ncols - 1;
1640 }
1641
1642 if (more_lines(spec, i, skipline)) {
1643 fputs(", \\", fp);
1644 }
1645
1646 fputc('\n', fp);
1647 }
1648
1649 /* supply the data to gnuplot inline, old style */
1650
1651 if (!spec->heredata && anydata) {
1652 if (spec->bars != NULL && spec->nbars > 0) {
1653 print_plotbars(spec, fp);
1654 }
1655 if (spec->data != NULL || spec->auxdata != NULL) {
1656 plotspec_print_data(spec, skipline, &miss, fp);
1657 }
1658 }
1659
1660 gretl_pop_c_numeric_locale();
1661
1662 if (png) {
1663 write_plot_bounding_box_request(fp);
1664 }
1665
1666 return miss;
1667 }
1668
set_loess_fit(GPT_SPEC * spec,int d,double q,gretl_matrix * x,gretl_matrix * y,gretl_matrix * yh)1669 static int set_loess_fit (GPT_SPEC *spec, int d, double q, gretl_matrix *x,
1670 gretl_matrix *y, gretl_matrix *yh)
1671 {
1672 int i, t, T = gretl_vector_get_length(y);
1673 const double *src;
1674 gretl_matrix *m;
1675
1676 m = gretl_matrix_alloc(T, 3);
1677 if (m == NULL) {
1678 return E_DATA;
1679 }
1680
1681 gretl_matrix_free(spec->data);
1682 spec->data = m;
1683
1684 for (i=0; i<3; i++) {
1685 src = i == 0 ? x->val : i == 1 ? y->val : yh->val;
1686 for (t=0; t<T; t++) {
1687 gretl_matrix_set(m, t, i, src[t]);
1688 }
1689 }
1690
1691 spec->data = m;
1692 spec->datacols = 3;
1693 spec->nobs = spec->okobs = T;
1694
1695 g_free(spec->lines[1].title);
1696 spec->lines[1].title = g_strdup_printf(_("loess fit, d = %d, q = %g"), d, q);
1697 spec->lines[1].pscale = 1.0;
1698 spec->lines[1].style = GP_STYLE_LINES;
1699 spec->lines[1].ncols = 2;
1700
1701 spec->fit = PLOT_FIT_LOESS;
1702
1703 return 0;
1704 }
1705
set_plotfit_formula(GPT_LINE * line,FitType f,const double * b,double x0,double pd)1706 static void set_plotfit_formula (GPT_LINE *line, FitType f, const double *b,
1707 double x0, double pd)
1708 {
1709 gchar *s = NULL;
1710 char x[32];
1711
1712 g_free(line->formula);
1713 line->formula = NULL;
1714 gretl_push_c_numeric_locale();
1715
1716 /* If x0 is not NA that indicates that we've calculated a fit
1717 against time (t = 1,2,3,...,T), and we have to transform the
1718 equation so that it produces correct fitted values given
1719 x-data in the form year[.fraction].
1720 */
1721 if (!na(x0)) {
1722 if (pd > 1) {
1723 sprintf(x, "(%.1f*(x-%.8g)+1)", pd, x0);
1724 } else {
1725 sprintf(x, "(x-%.8g+1)", x0);
1726 }
1727 } else {
1728 strcpy(x, "x");
1729 }
1730
1731 if (f == PLOT_FIT_OLS) {
1732 s = g_strdup_printf("%.8g + %.8g*%s", b[0], b[1], x);
1733 } else if (f == PLOT_FIT_QUADRATIC) {
1734 s = g_strdup_printf("%.8g + %.8g*%s + %.8g*%s**2", b[0], b[1], x, b[2], x);
1735 } else if (f == PLOT_FIT_CUBIC) {
1736 s = g_strdup_printf("%.8g + %.8g*%s + %.8g*%s**2 + %.8g*%s**3",
1737 b[0], b[1], x, b[2], x, b[3], x);
1738 } else if (f == PLOT_FIT_INVERSE) {
1739 s = g_strdup_printf("%.8g + %.8g/%s", b[0], b[1], x);
1740 } else if (f == PLOT_FIT_LOGLIN) {
1741 s = g_strdup_printf("exp(%.8g + %.8g*%s)", b[0], b[1], x);
1742 } else if (f == PLOT_FIT_LINLOG) {
1743 if (!na(x0)) {
1744 s = g_strdup_printf("%.8g + %.8g*log%s", b[0], b[1], x);
1745 } else {
1746 s = g_strdup_printf("%.8g + %.8g*log(x)", b[0], b[1]);
1747 }
1748 }
1749
1750 gretl_pop_c_numeric_locale();
1751 line->formula = s;
1752 }
1753
set_plotfit_line(GPT_LINE * line,FitType f,const double * b,double x0,double pd)1754 void set_plotfit_line (GPT_LINE *line, FitType f, const double *b,
1755 double x0, double pd)
1756 {
1757 char xc = (na(x0)) ? 'X' : 't';
1758 gchar *s = NULL;
1759
1760 g_free(line->title);
1761 line->title = NULL;
1762
1763 /* first compose the key string for the fitted line */
1764 if (f == PLOT_FIT_OLS) {
1765 s = g_strdup_printf("Y = %#.3g %c %#.3g%c", b[0],
1766 (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1767 } else if (f == PLOT_FIT_QUADRATIC) {
1768 s = g_strdup_printf("Y = %#.3g %c %#.3g%c %c %#.3g%c^2", b[0],
1769 (b[1] > 0)? '+' : '-', fabs(b[1]), xc,
1770 (b[2] > 0)? '+' : '-', fabs(b[2]), xc);
1771 } else if (f == PLOT_FIT_CUBIC) {
1772 s = g_strdup_printf("Y = %#.3g %c %#.3g%c %c %#.3g%c^2 %c %#.3g%c^3",
1773 b[0], (b[1] > 0)? '+' : '-', fabs(b[1]), xc,
1774 (b[2] > 0)? '+' : '-', fabs(b[2]), xc,
1775 (b[3] > 0)? '+' : '-', fabs(b[3]), xc);
1776 } else if (f == PLOT_FIT_INVERSE) {
1777 s = g_strdup_printf("Y = %#.3g %c %#.3g(1/%c)", b[0],
1778 (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1779 } else if (f == PLOT_FIT_LOGLIN) {
1780 s = g_strdup_printf("logY = %#.3g %c %#.3g%c", b[0],
1781 (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1782 if (xc == 't' && (pd == 1 || pd == 4 || pd == 12)) {
1783 /* display annual growth rate in title */
1784 double g = 100 * (pow(exp(b[1]), pd) - 1);
1785 gchar *gstr, *tmp;
1786
1787 gstr = g_strdup_printf("\\n(%s %.2f%%)", _("annual growth"), g);
1788 tmp = g_strdup_printf("%s%s", s, gstr);
1789 g_free(s);
1790 s = tmp;
1791 g_free(gstr);
1792 }
1793 } else if (f == PLOT_FIT_LINLOG) {
1794 s = g_strdup_printf("Y = %#.3g %c %#.3glog(%c)", b[0],
1795 (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1796 }
1797
1798 line->title = s;
1799
1800 /* then set the formula itself */
1801 set_plotfit_formula(line, f, b, x0, pd);
1802 }
1803
plotspec_set_fitted_line(GPT_SPEC * spec,FitType f,double x0)1804 static void plotspec_set_fitted_line (GPT_SPEC *spec, FitType f,
1805 double x0)
1806 {
1807 double pd = spec->pd;
1808 const double *b;
1809
1810 if (f == PLOT_FIT_OLS) {
1811 b = spec->b_ols->val;
1812 } else if (f == PLOT_FIT_QUADRATIC) {
1813 b = spec->b_quad->val;
1814 } else if (f == PLOT_FIT_CUBIC) {
1815 b = spec->b_cub->val;
1816 } else if (f == PLOT_FIT_INVERSE) {
1817 b = spec->b_inv->val;
1818 } else if (f == PLOT_FIT_LOGLIN) {
1819 b = spec->b_log->val;
1820 } else if (f == PLOT_FIT_LINLOG) {
1821 b = spec->b_linlog->val;
1822 } else {
1823 return;
1824 }
1825
1826 set_plotfit_line(&spec->lines[1], f, b, x0, pd);
1827
1828 spec->fit = f;
1829 spec->lines[1].pscale = 1.0;
1830 spec->lines[1].style = GP_STYLE_LINES;
1831 spec->lines[1].ncols = 0;
1832 }
1833
plotspec_add_fit(GPT_SPEC * spec,FitType f)1834 int plotspec_add_fit (GPT_SPEC *spec, FitType f)
1835 {
1836 gretl_matrix *y = NULL;
1837 gretl_matrix *X = NULL;
1838 gretl_matrix *b = NULL;
1839 gretl_matrix *yh = NULL;
1840 const double *px = spec->data->val;
1841 const double *py;
1842 int T = spec->okobs;
1843 double x0 = NADBL;
1844 double xt, q = 0.5;
1845 int d = 1;
1846 int i, t, k;
1847 int err = 0;
1848
1849 if ((spec->flags & GPT_TS) && f != PLOT_FIT_LOESS) {
1850 if (spec->pd == 1 || spec->pd == 4 || spec->pd == 12) {
1851 /* Annual, quarterly or monthly data: we'll estimate
1852 the fit against t = 1,2,3,...,T to give a more
1853 comprehensible displayed equation. So we have
1854 to record the initial x-value (e.g. 2008.25) to
1855 get the fitted line right on the plot.
1856 */
1857 x0 = px[0];
1858 }
1859 }
1860
1861 if ((f == PLOT_FIT_OLS && spec->b_ols != NULL) ||
1862 (f == PLOT_FIT_QUADRATIC && spec->b_quad != NULL) ||
1863 (f == PLOT_FIT_CUBIC && spec->b_cub != NULL) ||
1864 (f == PLOT_FIT_INVERSE && spec->b_inv != NULL) ||
1865 (f == PLOT_FIT_LOGLIN && spec->b_log != NULL) ||
1866 (f == PLOT_FIT_LINLOG && spec->b_linlog != NULL)) {
1867 /* just activate existing setup */
1868 plotspec_set_fitted_line(spec, f, x0);
1869 return 0;
1870 }
1871
1872 if (f == PLOT_FIT_OLS || f == PLOT_FIT_INVERSE ||
1873 f == PLOT_FIT_LOGLIN || f == PLOT_FIT_LINLOG) {
1874 k = 2;
1875 } else if (f == PLOT_FIT_QUADRATIC) {
1876 k = 3;
1877 } else if (f == PLOT_FIT_CUBIC) {
1878 k = 4;
1879 } else if (f == PLOT_FIT_LOESS) {
1880 k = 1;
1881 } else {
1882 return E_DATA;
1883 }
1884
1885 y = gretl_column_vector_alloc(T);
1886 X = gretl_matrix_alloc(T, k);
1887 if (y == NULL || X == NULL) {
1888 err = E_ALLOC;
1889 goto bailout;
1890 }
1891
1892 if (f != PLOT_FIT_LOESS) {
1893 b = gretl_column_vector_alloc(k);
1894 if (b == NULL) {
1895 err = E_ALLOC;
1896 goto bailout;
1897 }
1898 }
1899
1900 px = spec->data->val;
1901 py = px + spec->nobs;
1902
1903 i = 0;
1904 for (t=0; t<spec->nobs; t++) {
1905 if (!na(x0)) {
1906 /* use a 1-based time index */
1907 xt = t + 1;
1908 } else {
1909 xt = px[t];
1910 }
1911 if (!na(py[t]) && !na(xt)) {
1912 if (f == PLOT_FIT_LOGLIN) {
1913 /* y must be positive */
1914 if (py[t] > 0.0) {
1915 y->val[i] = log(py[t]);
1916 } else {
1917 gretl_errmsg_set(_("Non-positive values encountered"));
1918 err = E_DATA;
1919 goto bailout;
1920 }
1921 } else {
1922 y->val[i] = py[t];
1923 }
1924 if (f == PLOT_FIT_LOESS) {
1925 gretl_matrix_set(X, i, 0, xt);
1926 } else {
1927 gretl_matrix_set(X, i, 0, 1.0);
1928 if (f == PLOT_FIT_LINLOG) {
1929 /* x must be positive */
1930 if (xt > 0.0) {
1931 gretl_matrix_set(X, i, 1, log(xt));
1932 } else {
1933 gretl_errmsg_set(_("Non-positive values encountered"));
1934 err = E_DATA;
1935 goto bailout;
1936 }
1937 } else if (f == PLOT_FIT_INVERSE) {
1938 gretl_matrix_set(X, i, 1, 1.0 / xt);
1939 } else {
1940 gretl_matrix_set(X, i, 1, xt);
1941 }
1942 if (f == PLOT_FIT_QUADRATIC || f == PLOT_FIT_CUBIC) {
1943 gretl_matrix_set(X, i, 2, xt * xt);
1944 }
1945 if (f == PLOT_FIT_CUBIC) {
1946 gretl_matrix_set(X, i, 3, xt * xt * xt);
1947 }
1948 }
1949 i++;
1950 }
1951 }
1952
1953 if (f == PLOT_FIT_LOESS) {
1954 err = sort_pairs_by_x(X, y, NULL, spec->markers);
1955 if (!err) {
1956 yh = loess_fit(X, y, d, q, OPT_R, &err);
1957 }
1958 } else if (f == PLOT_FIT_LOGLIN) {
1959 double s2;
1960
1961 err = gretl_matrix_ols(y, X, b, NULL, NULL, &s2);
1962 if (!err) {
1963 b->val[0] += s2 / 2;
1964 }
1965 } else {
1966 err = gretl_matrix_ols(y, X, b, NULL, NULL, NULL);
1967 }
1968
1969 if (!err && spec->n_lines == 1) {
1970 err = plotspec_add_line(spec);
1971 }
1972
1973 if (!err) {
1974 if (f == PLOT_FIT_OLS) {
1975 spec->b_ols = b;
1976 b = NULL;
1977 } else if (f == PLOT_FIT_QUADRATIC) {
1978 spec->b_quad = b;
1979 b = NULL;
1980 } else if (f == PLOT_FIT_CUBIC) {
1981 spec->b_cub = b;
1982 b = NULL;
1983 } else if (f == PLOT_FIT_INVERSE) {
1984 spec->b_inv = b;
1985 b = NULL;
1986 } else if (f == PLOT_FIT_LOGLIN) {
1987 spec->b_log = b;
1988 b = NULL;
1989 } else if (f == PLOT_FIT_LINLOG) {
1990 spec->b_linlog = b;
1991 b = NULL;
1992 }
1993 }
1994
1995 if (!err) {
1996 if (f == PLOT_FIT_LOESS) {
1997 set_loess_fit(spec, d, q, X, y, yh);
1998 g_free(spec->lines[1].formula);
1999 spec->lines[1].formula = NULL;
2000 } else {
2001 plotspec_set_fitted_line(spec, f, x0);
2002 }
2003 }
2004
2005 bailout:
2006
2007 gretl_matrix_free(y);
2008 gretl_matrix_free(X);
2009 gretl_matrix_free(b);
2010 gretl_matrix_free(yh);
2011
2012 #if 0
2013 fprintf(stderr, "plotspec_add_fit: returning %d\n", err);
2014 #endif
2015
2016 return err;
2017 }
2018
2019 /* next: apparatus for producing shaded bars in time-series
2020 plots */
2021
2022 #define BDEBUG 0
2023
plotbars_new(int n)2024 static plotbars *plotbars_new (int n)
2025 {
2026 plotbars *bars = malloc(sizeof *bars);
2027
2028 if (bars != NULL) {
2029 bars->dx = doubles_array_new(n, 2);
2030 if (bars->dx == NULL) {
2031 free(bars);
2032 bars = NULL;
2033 } else {
2034 bars->n = n;
2035 bars->t1 = bars->t2 = 0;
2036 bars->ymin = bars->ymax = 0;
2037 }
2038 }
2039
2040 return bars;
2041 }
2042
2043 /* Read and check a plain text "plotbars" file. Such a file may
2044 contain comment lines starting with '#'; other than comments, each
2045 line should contain a pair of space-separated date strings in gretl
2046 monthly date format (YYYY:MM). These strings represent,
2047 respectively, the start and end of an episode of some kind
2048 (paradigm case: peak and trough of business cycle). Each pair may
2049 be used to draw a vertical bar on a time-series plot. The file can
2050 contain any number of such pairs. An example is provided in
2051 <prefix>/share/data/plotbars/nber.txt.
2052
2053 The dates are converted internally into the format, year plus
2054 decimal fraction of year, and may be used when plotting
2055 annual, quarterly or monthly time series.
2056 */
2057
parse_bars_file(const char * fname,int * err)2058 static plotbars *parse_bars_file (const char *fname,
2059 int *err)
2060 {
2061 FILE *fp;
2062 plotbars *bars = NULL;
2063 char line[128];
2064 int ncolon = 0;
2065 int ndash = 0;
2066 int nother = 0;
2067 int d1, d2, d3, d4;
2068
2069 fp = gretl_fopen(fname, "r");
2070 if (fp == NULL) {
2071 *err = E_FOPEN;
2072 return NULL;
2073 }
2074
2075 #if BDEBUG > 1
2076 fprintf(stderr, "parse_bars_file: '%s'\n", fname);
2077 #endif
2078
2079 /* first count the data lines */
2080
2081 while (fgets(line, sizeof line, fp)) {
2082 if (*line == '#' || string_is_blank(line)) {
2083 continue;
2084 } else if (sscanf(line, "%d:%d %d:%d", &d1, &d2, &d3, &d4) == 4) {
2085 ncolon++;
2086 } else if (sscanf(line, "%d-%d %d-%d", &d1, &d2, &d3, &d4) == 4) {
2087 ndash++;
2088 } else {
2089 nother++;
2090 }
2091 }
2092
2093 /* initial check and allocation */
2094
2095 if (nother > 0) {
2096 /* non-comment, non-blank, non-parseable cruft found */
2097 *err = E_DATA;
2098 } else if (ncolon == 0 && ndash == 0) {
2099 /* no parseable data found */
2100 *err = E_DATA;
2101 } else if (ncolon > 0 && ndash > 0) {
2102 /* unparseable mash-up found */
2103 *err = E_DATA;
2104 } else {
2105 bars = plotbars_new(ncolon + ndash);
2106 if (bars == NULL) {
2107 *err = E_ALLOC;
2108 }
2109 }
2110
2111 if (*err == 0) {
2112 double x0, x1;
2113 int i = 0;
2114
2115 rewind(fp);
2116
2117 /* now read, check, convert and record the date pairs */
2118
2119 while (fgets(line, sizeof line, fp) && !*err) {
2120 if (*line == '#' || string_is_blank(line)) {
2121 continue;
2122 }
2123 if (ncolon) {
2124 sscanf(line, "%d:%d %d:%d", &d1, &d2, &d3, &d4);
2125 } else {
2126 sscanf(line, "%d-%d %d-%d", &d1, &d2, &d3, &d4);
2127 }
2128 x0 = d1 + (d2 - 1.0) / 12;
2129 x1 = d3 + (d4 - 1.0) / 12;
2130 #if BDEBUG > 1
2131 fprintf(stderr, "%.4f %.4f\n", x0, x1);
2132 #endif
2133 if (x1 < x0) {
2134 *err = E_DATA;
2135 } else {
2136 bars->dx[i][0] = x0;
2137 bars->dx[i][1] = x1;
2138 i++;
2139 }
2140 }
2141 }
2142
2143 fclose(fp);
2144
2145 if (*err == E_DATA) {
2146 gretl_errmsg_set(_("Dates file does not conform to the specification"));
2147 }
2148
2149 if (*err && bars != NULL) {
2150 plotbars_free(bars);
2151 bars = NULL;
2152 }
2153
2154 return bars;
2155 }
2156
2157 /* output data representing vertical shaded bars: each
2158 bar is represented by a pair of rows */
2159
print_plotbars(GPT_SPEC * spec,FILE * fp)2160 static void print_plotbars (GPT_SPEC *spec, FILE *fp)
2161 {
2162 plotbars *bars = spec->bars;
2163 double y0 = bars->ymin;
2164 double y1 = bars->ymax;
2165 int i, started, stopped;
2166
2167 if (spec->heredata) {
2168 fputs("$bars << EOB\n", fp);
2169 }
2170
2171 for (i=0; i<bars->n; i++) {
2172 if (bars->dx[i][1] < bars->t1) {
2173 continue;
2174 }
2175 if (bars->dx[i][0] > bars->t2) {
2176 break;
2177 }
2178 started = stopped = 0;
2179 if (bars->dx[i][0] >= bars->t1) {
2180 /* start is in range */
2181 fprintf(fp, "%.3f %.10g %.10g\n", bars->dx[i][0], y0, y1);
2182 started = 1;
2183 }
2184 if (bars->dx[i][1] <= bars->t2) {
2185 /* stop is in range */
2186 if (!started) {
2187 /* but the start was not */
2188 fprintf(fp, "%.3f %.10g %.10g\n", bars->t1, y0, y1);
2189 }
2190 fprintf(fp, "%.3f %.10g %.10g\n", bars->dx[i][1], y0, y1);
2191 if (!spec->heredata) {
2192 fputs("e\n", fp);
2193 }
2194 stopped = 1;
2195 }
2196 if (started && !stopped) {
2197 /* truncated */
2198 fprintf(fp, "%.3f %.10g %.10g\n", bars->t2, y0, y1);
2199 if (!spec->heredata) {
2200 fputs("e\n", fp);
2201 }
2202 }
2203 }
2204
2205 if (spec->heredata) {
2206 fputs("EOB\n", fp);
2207 }
2208 }
2209
2210 /* output special plot lines for creating vertical shaded bars
2211 in a time-series graph: the corresponding data will take
2212 the form x:ymin:ymax */
2213
print_bars_header(GPT_SPEC * spec,FILE * fp)2214 static void print_bars_header (GPT_SPEC *spec, FILE *fp)
2215 {
2216 char cstr[8];
2217 int i;
2218
2219 print_rgb_hash(cstr, get_shadecolor());
2220
2221 if (spec->heredata) {
2222 int j = 0;
2223
2224 for (i=0; i<spec->nbars; i++) {
2225 fprintf(fp, "'$bars' every ::%d::%d using 1:2:3 notitle lc rgb \"%s\" "
2226 "w filledcurve, \\\n", j, j+1, cstr);
2227 j += 2;
2228 }
2229 } else {
2230 for (i=0; i<spec->nbars; i++) {
2231 fprintf(fp, "'-' using 1:2:3 notitle lc rgb \"%s\" w filledcurve, \\\n",
2232 cstr);
2233 }
2234 }
2235 }
2236
print_filledcurve_color(FILE * fp)2237 static void print_filledcurve_color (FILE *fp)
2238 {
2239 char cstr[8];
2240
2241 print_rgb_hash(cstr, get_shadecolor());
2242 fprintf(fp, "lc rgb \"%s\" ", cstr);
2243 }
2244
2245 /* given the info in @bars, calculate how many of its start-stop
2246 pairs fall (at least partially) within the current x-axis
2247 range */
2248
n_bars_shown(double xmin,double xmax,plotbars * bars)2249 static int n_bars_shown (double xmin, double xmax, plotbars *bars)
2250 {
2251 double **dx = bars->dx;
2252 int i, n = 0;
2253
2254 for (i=0; i<bars->n; i++) {
2255 if (dx[i][1] < xmin) {
2256 continue;
2257 } else if (dx[i][0] > xmax) {
2258 break;
2259 } else if (dx[i][0] >= xmin || dx[i][1] <= xmax) {
2260 n++;
2261 }
2262 }
2263
2264 bars->t1 = xmin;
2265 bars->t2 = xmax;
2266
2267 #if BDEBUG
2268 fprintf(stderr, "n_bars_shown: n = %d\n", n);
2269 #endif
2270
2271 return n;
2272 }
2273
2274 /* Given the limits of the data area of a plot, @xmin et al,
2275 and the name of a plain text file containing "bars"
2276 information in the form of a set of start-stop pairs,
2277 try attaching this information to @spec. We fail if
2278 there's something wrong with the info in the file, or
2279 if none of the bars defined therein would be visible
2280 in the current range, @xmin to @xmax.
2281 */
2282
plotspec_add_bars_info(GPT_SPEC * spec,double xmin,double xmax,double ymin,double ymax,const char * fname)2283 int plotspec_add_bars_info (GPT_SPEC *spec,
2284 double xmin, double xmax,
2285 double ymin, double ymax,
2286 const char *fname)
2287 {
2288 plotbars *bars = NULL;
2289 int err = 0;
2290
2291 if (spec->bars != NULL) {
2292 /* clear out any stale info */
2293 plotbars_free(bars);
2294 spec->bars = NULL;
2295 spec->nbars = 0;
2296 }
2297
2298 bars = parse_bars_file(fname, &err);
2299
2300 if (!err) {
2301 int n = n_bars_shown(xmin, xmax, bars);
2302
2303 #if BDEBUG
2304 fprintf(stderr, "plotspec_add_bars_info:\n"
2305 " xmin=%g, xmax=%g, n=%d, ymin=%g, ymax=%g\n",
2306 xmin, xmax, n, ymin, ymax);
2307 #endif
2308 if (n > 0) {
2309 spec->bars = bars;
2310 bars->ymin = ymin;
2311 bars->ymax = ymax;
2312 spec->nbars = n;
2313 } else {
2314 plotbars_free(bars);
2315 }
2316 }
2317
2318 return err;
2319 }
2320
2321 /* the following is a public interface because it's also
2322 used when reconstituting @spec from a gnuplot command
2323 file.
2324 */
2325
plotspec_allocate_bars(GPT_SPEC * spec)2326 int plotspec_allocate_bars (GPT_SPEC *spec)
2327 {
2328 int err = 0;
2329
2330 spec->bars = plotbars_new(spec->nbars);
2331 if (spec->bars == NULL) {
2332 err = E_ALLOC;
2333 }
2334
2335 return err;
2336 }
2337
2338 /* for use when reconstituting @spec from a gnuplot
2339 command file: set the start/stop values for a
2340 particular bar @i.
2341 */
2342
plotspec_set_bar_info(GPT_SPEC * spec,int i,double t1,double t2)2343 int plotspec_set_bar_info (GPT_SPEC *spec, int i,
2344 double t1, double t2)
2345 {
2346 if (i < spec->bars->n) {
2347 spec->bars->dx[i][0] = t1;
2348 spec->bars->dx[i][1] = t2;
2349 return 0;
2350 } else {
2351 return E_DATA;
2352 }
2353 }
2354
plotspec_set_bars_limits(GPT_SPEC * spec,double t1,double t2,double ymin,double ymax)2355 void plotspec_set_bars_limits (GPT_SPEC *spec,
2356 double t1, double t2,
2357 double ymin, double ymax)
2358 {
2359 if (spec->bars != NULL) {
2360 spec->bars->t1 = t1;
2361 spec->bars->t2 = t2;
2362 spec->bars->ymin = ymin;
2363 spec->bars->ymax = ymax;
2364 }
2365 }
2366
plotspec_remove_bars(GPT_SPEC * spec)2367 void plotspec_remove_bars (GPT_SPEC *spec)
2368 {
2369 plotbars_free(spec->bars);
2370 spec->bars = NULL;
2371 spec->nbars = 0;
2372 }
2373