1 /*
2 Qalculate
3
4 Copyright (C) 2003-2007, 2008, 2016-2019 Hanna Knutsson (hanna.knutsson@protonmail.com)
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 */
11
12 #include "support.h"
13
14 #include "Calculator.h"
15 #include "util.h"
16 #include "MathStructure.h"
17
18 #include <locale.h>
19 #include <unistd.h>
20 #include <time.h>
21 #include <utime.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <dirent.h>
26 #include <vector>
27
28 using std::string;
29 using std::vector;
30 using std::cout;
31 using std::endl;
32
PlotParameters()33 PlotParameters::PlotParameters() {
34 auto_y_min = true;
35 auto_x_min = true;
36 auto_y_max = true;
37 auto_x_max = true;
38 y_log = false;
39 x_log = false;
40 y_log_base = 10;
41 x_log_base = 10;
42 grid = false;
43 color = true;
44 linewidth = -1;
45 show_all_borders = false;
46 legend_placement = PLOT_LEGEND_TOP_RIGHT;
47 }
PlotDataParameters()48 PlotDataParameters::PlotDataParameters() {
49 yaxis2 = false;
50 xaxis2 = false;
51 style = PLOT_STYLE_LINES;
52 smoothing = PLOT_SMOOTHING_NONE;
53 test_continuous = false;
54 }
55
canPlot()56 bool Calculator::canPlot() {
57 #ifdef HAVE_GNUPLOT_CALL
58 # ifdef _WIN32
59 LPSTR lpFilePart;
60 char filename[MAX_PATH];
61 return SearchPath(NULL, "gnuplot", ".exe", MAX_PATH, filename, &lpFilePart);
62 # else
63 FILE *pipe = popen("gnuplot - 2>/dev/null", "w");
64 if(!pipe) return false;
65 return pclose(pipe) == 0;
66 # endif
67 #else
68 return false;
69 #endif
70 }
71
parse_and_precalculate_plot(string & expression,MathStructure & mstruct,const ParseOptions & po,EvaluationOptions & eo)72 void parse_and_precalculate_plot(string &expression, MathStructure &mstruct, const ParseOptions &po, EvaluationOptions &eo) {
73 eo.approximation = APPROXIMATION_APPROXIMATE;
74 ParseOptions po2 = po;
75 po2.read_precision = DONT_READ_PRECISION;
76 eo.parse_options = po2;
77 eo.interval_calculation = INTERVAL_CALCULATION_NONE;
78 mstruct = CALCULATOR->parse(expression, po2);
79 MathStructure mbak(mstruct);
80 eo.calculate_functions = false;
81 eo.expand = false;
82 CALCULATOR->beginTemporaryStopMessages();
83 mstruct.eval(eo);
84 int im = 0;
85 if(CALCULATOR->endTemporaryStopMessages(NULL, &im) > 0 || im > 0) mstruct = mbak;
86 eo.calculate_functions = true;
87 eo.expand = true;
88 }
89
expressionToPlotVector(string expression,const MathStructure & min,const MathStructure & max,int steps,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)90 MathStructure Calculator::expressionToPlotVector(string expression, const MathStructure &min, const MathStructure &max, int steps, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
91 Variable *v = getActiveVariable(x_var);
92 MathStructure x_mstruct;
93 if(v) x_mstruct = v;
94 else x_mstruct = x_var;
95 EvaluationOptions eo;
96 MathStructure mparse;
97 if(msecs > 0) startControl(msecs);
98 beginTemporaryStopIntervalArithmetic();
99 parse_and_precalculate_plot(expression, mparse, po, eo);
100 beginTemporaryStopMessages();
101 MathStructure y_vector(mparse.generateVector(x_mstruct, min, max, steps, x_vector, eo));
102 endTemporaryStopMessages();
103 endTemporaryStopIntervalArithmetic();
104 if(msecs > 0) {
105 if(aborted()) error(true, _("It took too long to generate the plot data."), NULL);
106 stopControl();
107 }
108 if(y_vector.size() == 0) {
109 error(true, _("Unable to generate plot data with current min, max and sampling rate."), NULL);
110 }
111 return y_vector;
112 }
expressionToPlotVector(string expression,float min,float max,int steps,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)113 MathStructure Calculator::expressionToPlotVector(string expression, float min, float max, int steps, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
114 MathStructure min_mstruct(min), max_mstruct(max);
115 ParseOptions po2 = po;
116 po2.read_precision = DONT_READ_PRECISION;
117 MathStructure y_vector(expressionToPlotVector(expression, min_mstruct, max_mstruct, steps, x_vector, x_var, po2, msecs));
118 return y_vector;
119 }
expressionToPlotVector(string expression,const MathStructure & min,const MathStructure & max,const MathStructure & step,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)120 MathStructure Calculator::expressionToPlotVector(string expression, const MathStructure &min, const MathStructure &max, const MathStructure &step, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
121 Variable *v = getActiveVariable(x_var);
122 MathStructure x_mstruct;
123 if(v) x_mstruct = v;
124 else x_mstruct = x_var;
125 EvaluationOptions eo;
126 MathStructure mparse;
127 if(msecs > 0) startControl(msecs);
128 beginTemporaryStopIntervalArithmetic();
129 parse_and_precalculate_plot(expression, mparse, po, eo);
130 beginTemporaryStopMessages();
131 MathStructure y_vector(mparse.generateVector(x_mstruct, min, max, step, x_vector, eo));
132 endTemporaryStopMessages();
133 endTemporaryStopIntervalArithmetic();
134 if(msecs > 0) {
135 if(aborted()) error(true, _("It took too long to generate the plot data."), NULL);
136 stopControl();
137 }
138 if(y_vector.size() == 0) {
139 error(true, _("Unable to generate plot data with current min, max and step size."), NULL);
140 }
141 return y_vector;
142 }
expressionToPlotVector(string expression,float min,float max,float step,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)143 MathStructure Calculator::expressionToPlotVector(string expression, float min, float max, float step, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
144 MathStructure min_mstruct(min), max_mstruct(max), step_mstruct(step);
145 ParseOptions po2 = po;
146 po2.read_precision = DONT_READ_PRECISION;
147 MathStructure y_vector(expressionToPlotVector(expression, min_mstruct, max_mstruct, step_mstruct, x_vector, x_var, po2, msecs));
148 return y_vector;
149 }
expressionToPlotVector(string expression,const MathStructure & x_vector,string x_var,const ParseOptions & po,int msecs)150 MathStructure Calculator::expressionToPlotVector(string expression, const MathStructure &x_vector, string x_var, const ParseOptions &po, int msecs) {
151 Variable *v = getActiveVariable(x_var);
152 MathStructure x_mstruct;
153 if(v) x_mstruct = v;
154 else x_mstruct = x_var;
155 EvaluationOptions eo;
156 MathStructure mparse;
157 if(msecs > 0) startControl(msecs);
158 beginTemporaryStopIntervalArithmetic();
159 parse_and_precalculate_plot(expression, mparse, po, eo);
160 beginTemporaryStopMessages();
161 MathStructure y_vector(mparse.generateVector(x_mstruct, x_vector, eo).eval(eo));
162 endTemporaryStopMessages();
163 endTemporaryStopIntervalArithmetic();
164 if(msecs > 0) {
165 if(aborted()) error(true, _("It took too long to generate the plot data."), NULL);
166 stopControl();
167 }
168 return y_vector;
169 }
170
plotVectors(PlotParameters * param,const vector<MathStructure> & y_vectors,const vector<MathStructure> & x_vectors,vector<PlotDataParameters * > & pdps,bool persistent,int msecs)171 bool Calculator::plotVectors(PlotParameters *param, const vector<MathStructure> &y_vectors, const vector<MathStructure> &x_vectors, vector<PlotDataParameters*> &pdps, bool persistent, int msecs) {
172
173 string homedir = getLocalTmpDir();
174 recursiveMakeDir(homedir);
175
176 string commandline_extra;
177 string title;
178
179 if(!param) {
180 PlotParameters pp;
181 param = &pp;
182 }
183
184 string plot;
185
186 if(param->filename.empty()) {
187 if(!param->color) {
188 commandline_extra += " -mono";
189 }
190 plot += "set terminal pop\n";
191 } else {
192 persistent = true;
193 if(param->filetype == PLOT_FILETYPE_AUTO) {
194 size_t i = param->filename.rfind(".");
195 if(i == string::npos) {
196 param->filetype = PLOT_FILETYPE_PNG;
197 error(false, _("No extension in file name. Saving as PNG image."), NULL);
198 } else {
199 string ext = param->filename.substr(i + 1, param->filename.length() - (i + 1));
200 if(ext == "png") {
201 param->filetype = PLOT_FILETYPE_PNG;
202 } else if(ext == "ps") {
203 param->filetype = PLOT_FILETYPE_PS;
204 } else if(ext == "pdf") {
205 param->filetype = PLOT_FILETYPE_PDF;
206 } else if(ext == "eps") {
207 param->filetype = PLOT_FILETYPE_EPS;
208 } else if(ext == "svg") {
209 param->filetype = PLOT_FILETYPE_SVG;
210 } else if(ext == "fig") {
211 param->filetype = PLOT_FILETYPE_FIG;
212 } else if(ext == "tex") {
213 param->filetype = PLOT_FILETYPE_LATEX;
214 } else {
215 param->filetype = PLOT_FILETYPE_PNG;
216 error(false, _("Unknown extension in file name. Saving as PNG image."), NULL);
217 }
218 }
219 }
220 plot += "set terminal ";
221 switch(param->filetype) {
222 case PLOT_FILETYPE_FIG: {
223 plot += "fig ";
224 if(param->color) {
225 plot += "color";
226 } else {
227 plot += "monochrome";
228 }
229 break;
230 }
231 case PLOT_FILETYPE_SVG: {
232 plot += "svg";
233 break;
234 }
235 case PLOT_FILETYPE_LATEX: {
236 plot += "latex ";
237 break;
238 }
239 case PLOT_FILETYPE_PS: {
240 plot += "postscript ";
241 if(param->color) {
242 plot += "color";
243 } else {
244 plot += "monochrome";
245 }
246 plot += " \"Times\"";
247 break;
248 }
249 case PLOT_FILETYPE_PDF: {
250 plot += "pdf ";
251 if(param->color) {
252 plot += "color";
253 } else {
254 plot += "monochrome";
255 }
256 break;
257 }
258 case PLOT_FILETYPE_EPS: {
259 plot += "postscript eps ";
260 if(param->color) {
261 plot += "color";
262 } else {
263 plot += "monochrome";
264 }
265 plot += " \"Times\"";
266 break;
267 }
268 default: {
269 plot += "png ";
270 break;
271 }
272
273 }
274 plot += "\nset output \"";
275 plot += param->filename;
276 plot += "\"\n";
277 }
278
279 plot += "set termoption noenhanced\n";
280
281 switch(param->legend_placement) {
282 case PLOT_LEGEND_NONE: {plot += "set nokey\n"; break;}
283 case PLOT_LEGEND_TOP_LEFT: {plot += "set key top left\n"; break;}
284 case PLOT_LEGEND_TOP_RIGHT: {plot += "set key top right\n"; break;}
285 case PLOT_LEGEND_BOTTOM_LEFT: {plot += "set key bottom left\n"; break;}
286 case PLOT_LEGEND_BOTTOM_RIGHT: {plot += "set key bottom right\n"; break;}
287 case PLOT_LEGEND_BELOW: {plot += "set key below\n"; break;}
288 case PLOT_LEGEND_OUTSIDE: {plot += "set key outside\n"; break;}
289 }
290 if(!param->x_label.empty()) {
291 title = param->x_label;
292 gsub("\"", "\\\"", title);
293 plot += "set xlabel \"";
294 plot += title;
295 plot += "\"\n";
296 }
297 if(!param->y_label.empty()) {
298 string title = param->y_label;
299 gsub("\"", "\\\"", title);
300 plot += "set ylabel \"";
301 plot += title;
302 plot += "\"\n";
303 }
304 if(!param->title.empty()) {
305 title = param->title;
306 gsub("\"", "\\\"", title);
307 plot += "set title \"";
308 plot += title;
309 plot += "\"\n";
310 }
311 if(param->grid) {
312 plot += "set grid\n";
313
314 }
315 if(!param->auto_y_min || !param->auto_y_max) {
316 plot += "set yrange [";
317 if(!param->auto_y_min) plot += d2s(param->y_min);
318 plot += ":";
319 if(!param->auto_y_max) plot += d2s(param->y_max);
320 plot += "]";
321 plot += "\n";
322 }
323 if(param->x_log) {
324 plot += "set logscale x ";
325 plot += i2s(param->x_log_base);
326 plot += "\n";
327 }
328 if(param->y_log) {
329 plot += "set logscale y ";
330 plot += i2s(param->y_log_base);
331 plot += "\n";
332 }
333 if(param->show_all_borders) {
334 plot += "set border 15\n";
335 } else {
336 bool xaxis2 = false, yaxis2 = false;
337 for(size_t i = 0; i < pdps.size(); i++) {
338 if(pdps[i] && pdps[i]->xaxis2) {
339 xaxis2 = true;
340 }
341 if(pdps[i] && pdps[i]->yaxis2) {
342 yaxis2 = true;
343 }
344 }
345 if(xaxis2 && yaxis2) {
346 plot += "set border 15\nset x2tics\nset y2tics\n";
347 } else if(xaxis2) {
348 plot += "set border 7\nset x2tics\n";
349 } else if(yaxis2) {
350 plot += "set border 11\nset y2tics\n";
351 } else {
352 plot += "set border 3\n";
353 }
354 plot += "set xtics nomirror\nset ytics nomirror\n";
355 }
356 size_t samples = 1000;
357 for(size_t i = 0; i < y_vectors.size(); i++) {
358 if(!y_vectors[i].isUndefined()) {
359 if(y_vectors[i].size() > 3000) {
360 samples = 6000;
361 break;
362 }
363 if(y_vectors[i].size() * 2 > samples) samples = y_vectors[i].size() * 2;
364 }
365 }
366 plot += "set samples ";
367 plot += i2s(samples);
368 plot += "\n";
369 plot += "plot ";
370 for(size_t i = 0; i < y_vectors.size(); i++) {
371 if(!y_vectors[i].isUndefined()) {
372 if(i != 0) {
373 plot += ",";
374 }
375 string filename = "gnuplot_data";
376 filename += i2s(i + 1);
377 filename = buildPath(homedir, filename);
378 #ifdef _WIN32
379 gsub("\\", "\\\\", filename);
380 #endif
381 plot += "\"";
382 plot += filename;
383 plot += "\"";
384 if(i < pdps.size()) {
385 switch(pdps[i]->smoothing) {
386 case PLOT_SMOOTHING_UNIQUE: {plot += " smooth unique"; break;}
387 case PLOT_SMOOTHING_CSPLINES: {plot += " smooth csplines"; break;}
388 case PLOT_SMOOTHING_BEZIER: {plot += " smooth bezier"; break;}
389 case PLOT_SMOOTHING_SBEZIER: {plot += " smooth sbezier"; break;}
390 default: {}
391 }
392 if(pdps[i]->xaxis2 && pdps[i]->yaxis2) {
393 plot += " axis x2y2";
394 } else if(pdps[i]->xaxis2) {
395 plot += " axis x2y1";
396 } else if(pdps[i]->yaxis2) {
397 plot += " axis x1y2";
398 }
399 if(!pdps[i]->title.empty()) {
400 title = pdps[i]->title;
401 gsub("\"", "\\\"", title);
402 plot += " title \"";
403 plot += title;
404 plot += "\"";
405 }
406 switch(pdps[i]->style) {
407 case PLOT_STYLE_LINES: {plot += " with lines"; break;}
408 case PLOT_STYLE_POINTS: {plot += " with points"; break;}
409 case PLOT_STYLE_POINTS_LINES: {plot += " with linespoints"; break;}
410 case PLOT_STYLE_BOXES: {plot += " with boxes"; break;}
411 case PLOT_STYLE_HISTOGRAM: {plot += " with histeps"; break;}
412 case PLOT_STYLE_STEPS: {plot += " with steps"; break;}
413 case PLOT_STYLE_CANDLESTICKS: {plot += " with candlesticks"; break;}
414 case PLOT_STYLE_DOTS: {plot += " with dots"; break;}
415 }
416 if(param->linewidth < 1) {
417 plot += " lw 2";
418 } else {
419 plot += " lw ";
420 plot += i2s(param->linewidth);
421 }
422 }
423 }
424 }
425 plot += "\n";
426
427 string plot_data;
428 PrintOptions po;
429 po.number_fraction_format = FRACTION_DECIMAL;
430 po.interval_display = INTERVAL_DISPLAY_MIDPOINT;
431 po.decimalpoint_sign = ".";
432 po.comma_sign = ",";
433 for(size_t serie = 0; serie < y_vectors.size(); serie++) {
434 if(!y_vectors[serie].isUndefined()) {
435 string filename = "gnuplot_data";
436 filename += i2s(serie + 1);
437 string filepath = buildPath(homedir, filename);
438 FILE *fdata = fopen(filepath.c_str(), "w+");
439 if(!fdata) {
440 error(true, _("Could not create temporary file %s"), filepath.c_str(), NULL);
441 return false;
442 }
443 plot_data = "";
444 int non_numerical = 0, non_real = 0;
445 //string str = "";
446 if(msecs > 0) startControl(msecs);
447 ComparisonResult ct1 = COMPARISON_RESULT_EQUAL, ct2 = COMPARISON_RESULT_EQUAL;
448 size_t last_index = string::npos, last_index2 = string::npos;
449 bool check_continuous = pdps[serie]->test_continuous && (pdps[serie]->style == PLOT_STYLE_LINES || pdps[serie]->style == PLOT_STYLE_POINTS_LINES);
450 bool prev_failed = false;
451 for(size_t i = 1; i <= y_vectors[serie].countChildren(); i++) {
452 ComparisonResult ct = COMPARISON_RESULT_UNKNOWN;
453 bool invalid_nr = false, b_imagzero_x = false, b_imagzero_y = false;
454 if(!y_vectors[serie].getChild(i)->isNumber()) {
455 invalid_nr = true;
456 non_numerical++;
457 //if(non_numerical == 1) str = y_vectors[serie].getChild(i)->print(po);
458 } else if(!y_vectors[serie].getChild(i)->number().isReal()) {
459 b_imagzero_y = testComplexZero(&y_vectors[serie].getChild(i)->number(), y_vectors[serie].getChild(i)->number().internalImaginary());
460 if(!b_imagzero_y) {
461 invalid_nr = true;
462 non_real++;
463 //if(non_numerical + non_real == 1) str = y_vectors[serie].getChild(i)->print(po);
464 }
465 }
466 if(serie < x_vectors.size() && !x_vectors[serie].isUndefined() && x_vectors[serie].countChildren() == y_vectors[serie].countChildren()) {
467 if(!x_vectors[serie].getChild(i)->isNumber()) {
468 invalid_nr = true;
469 non_numerical++;
470 //if(non_numerical == 1) str = x_vectors[serie].getChild(i)->print(po);
471 } else if(!x_vectors[serie].getChild(i)->number().isReal()) {
472 b_imagzero_x = testComplexZero(&x_vectors[serie].getChild(i)->number(), x_vectors[serie].getChild(i)->number().internalImaginary());
473 if(!b_imagzero_x) {
474 invalid_nr = true;
475 non_real++;
476 //if(non_numerical + non_real == 1) str = x_vectors[serie].getChild(i)->print(po);
477 }
478 }
479 if(!invalid_nr) {
480 if(b_imagzero_y) plot_data += x_vectors[serie].getChild(i)->number().realPart().print(po);
481 else plot_data += x_vectors[serie].getChild(i)->print(po);
482 plot_data += " ";
483 }
484 }
485 if(!invalid_nr) {
486 if(check_continuous && !prev_failed) {
487 if(i == 1 || ct2 == COMPARISON_RESULT_UNKNOWN) ct = COMPARISON_RESULT_EQUAL;
488 else ct = y_vectors[serie].getChild(i - 1)->number().compare(y_vectors[serie].getChild(i)->number());
489 if((ct == COMPARISON_RESULT_GREATER || ct == COMPARISON_RESULT_LESS) && (ct1 == COMPARISON_RESULT_GREATER || ct1 == COMPARISON_RESULT_LESS) && (ct2 == COMPARISON_RESULT_GREATER || ct2 == COMPARISON_RESULT_LESS) && ct1 != ct2 && ct != ct2) {
490 if(last_index2 != string::npos) plot_data.insert(last_index2 + 1, " \n");
491 }
492 }
493 if(b_imagzero_x) plot_data += y_vectors[serie].getChild(i)->number().realPart().print(po);
494 else plot_data += y_vectors[serie].getChild(i)->print(po);
495 plot_data += "\n";
496 prev_failed = false;
497 } else if(!prev_failed) {
498 ct = COMPARISON_RESULT_UNKNOWN;
499 plot_data += " \n";
500 prev_failed = true;
501 }
502 last_index2 = last_index;
503 last_index = plot_data.length() - 1;
504 ct1 = ct2;
505 ct2 = ct;
506 if(aborted()) {
507 fclose(fdata);
508 if(msecs > 0) {
509 error(true, _("It took too long to generate the plot data."), NULL);
510 stopControl();
511 }
512 return false;
513 }
514 }
515 if(msecs > 0) stopControl();
516 /*if(non_numerical > 0 || non_real > 0) {
517 string stitle;
518 if(serie < pdps.size() && !pdps[serie]->title.empty()) {
519 stitle = pdps[serie]->title.c_str();
520 } else {
521 stitle = i2s(serie).c_str();
522 }
523 if(non_numerical > 0) {
524 error(true, _("Series %s contains non-numerical data (\"%s\" first of %s) which can not be properly plotted."), stitle.c_str(), str.c_str(), i2s(non_numerical).c_str(), NULL);
525 } else {
526 error(true, _("Series %s contains non-real data (\"%s\" first of %s) which can not be properly plotted."), stitle.c_str(), str.c_str(), i2s(non_real).c_str(), NULL);
527 }
528 }*/
529 fputs(plot_data.c_str(), fdata);
530 fflush(fdata);
531 fclose(fdata);
532 }
533 }
534
535 return invokeGnuplot(plot, commandline_extra, persistent);
536 }
537 #ifdef HAVE_GNUPLOT_CALL
invokeGnuplot(string commands,string commandline_extra,bool persistent)538 bool Calculator::invokeGnuplot(string commands, string commandline_extra, bool persistent) {
539 FILE *pipe = NULL;
540 if(!b_gnuplot_open || !gnuplot_pipe || persistent || commandline_extra != gnuplot_cmdline) {
541 if(!persistent) {
542 closeGnuplot();
543 }
544 string commandline = "gnuplot";
545 if(persistent) {
546 commandline += " -persist";
547 }
548 commandline += commandline_extra;
549 #ifdef _WIN32
550 commandline += " - 2>nul";
551 pipe = _popen(commandline.c_str(), "w");
552 #else
553 commandline += " - 2>/dev/null";
554 pipe = popen(commandline.c_str(), "w");
555 #endif
556 if(!pipe) {
557 error(true, _("Failed to invoke gnuplot. Make sure that you have gnuplot installed in your path."), NULL);
558 return false;
559 }
560 if(!persistent && pipe) {
561 gnuplot_pipe = pipe;
562 b_gnuplot_open = true;
563 gnuplot_cmdline = commandline_extra;
564 }
565 } else {
566 pipe = gnuplot_pipe;
567 }
568 if(!pipe) {
569 return false;
570 }
571 if(!persistent) {
572 fputs("clear\n", pipe);
573 fputs("reset\n", pipe);
574 }
575 fputs(commands.c_str(), pipe);
576 fflush(pipe);
577 if(persistent) {
578 return pclose(pipe) == 0;
579 }
580 return true;
581 }
582 #else
invokeGnuplot(string,string,bool)583 bool Calculator::invokeGnuplot(string, string, bool) {
584 return false;
585 }
586 #endif
closeGnuplot()587 bool Calculator::closeGnuplot() {
588 #ifdef HAVE_GNUPLOT_CALL
589 if(gnuplot_pipe) {
590 # ifdef _WIN32
591 int rv = _pclose(gnuplot_pipe);
592 # else
593 int rv = pclose(gnuplot_pipe);
594 # endif
595 gnuplot_pipe = NULL;
596 b_gnuplot_open = false;
597 return rv == 0;
598 }
599 gnuplot_pipe = NULL;
600 b_gnuplot_open = false;
601 return true;
602 #else
603 return false;
604 #endif
605 }
gnuplotOpen()606 bool Calculator::gnuplotOpen() {
607 return b_gnuplot_open && gnuplot_pipe;
608 }
609
610