Developer Documentation
Gnuplot.cc
1
2//
3// A C++ interface to gnuplot.
4//
5// This is a direct translation from the C interface
6// written by N. Devillard (which is available from
7// http://ndevilla.free.fr/gnuplot/).
8//
9// As in the C interface this uses pipes and so wont
10// run on a system that does'nt have POSIX pipe
11// support
12//
13// Rajarshi Guha
14// <rajarshi@presidency.com>
15//
16// 07/03/03
17//
19
20#include "Gnuplot.hh"
21#include <stdarg.h>
22#ifdef WIN32
23# include <io.h>
24#else
25# include <fcntl.h> // X_OK
26# include <unistd.h> // access
27# define PATH_MAXNAMESZ 4096
28#endif
29#include <iostream>
30#include <fstream>
31#include <sstream>
32#include <list>
33#include <algorithm>
34
35#if defined(WIN32)
36# define pclose _pclose
37# define popen _popen
38# define access _access
39# define ACCESS_OK 0
40# define PATH_SEP ";"
41# define MKTEMP_AND_CHECK_FAILED(name) (_mktemp(name) == nullptr)
42#else
43# define ACCESS_OK X_OK
44# define PATH_SEP ":"
45# define MKTEMP_AND_CHECK_FAILED(name) (mkstemp(name) == -1)
46#endif
47
48#ifndef WIN32
49
50#include <string.h>
51#else
52 #ifdef __MINGW32__
53 #include <stdlib.h>
54 #include <string.h>
55 #endif
56#endif
57
58using namespace std;
59
61//
62// A string tokenizer taken from
63// http://www.sunsite.ualberta.ca/
64// Documentation/Gnu/libstdc++-2.90.8/html/21_strings/stringtok_std_h.txt
65//
67
68template <typename Container>
69void
70stringtok (Container &container, string const &in,
71 const char * const delimiters = " \t\n")
72{
73 const string::size_type len = in.length();
74
75 string::size_type i = 0;
76
77 while ( i < len )
78 {
79 // eat leading whitespace
80 i = in.find_first_not_of (delimiters, i);
81 if (i == string::npos)
82 return; // nothing left but white space
83
84 // find the end of the token
85 string::size_type j = in.find_first_of (delimiters, i);
86
87 // push token
88 if (j == string::npos)
89 {
90 container.push_back (in.substr(i));
91 return;
92 } else
93 container.push_back (in.substr(i, j-i));
94
95 // set up for next loop
96 i = j + 1;
97 }
98}
99
100// ----------------------------------------------------------------------------
101
102#ifdef WIN32
103 std::string Gnuplot::gnuplot_executable_ = "pgnuplot.exe";
104#else
105 std::string Gnuplot::gnuplot_executable_ = "gnuplot";
106#endif
107
108// ----------------------------------------------------------------------------
109
111{
112 init();
113 set_style("points");
114}
115
116// ----------------------------------------------------------------------------
117
118Gnuplot::Gnuplot(const string &style)
119{
120 init();
121 set_style(style);
122}
123
124// ----------------------------------------------------------------------------
125
126Gnuplot::Gnuplot(const string &title,
127 const string &style,
128 const string &labelx, const string &labely,
129 const vector<double>& x, const vector<double>& y)
130{
131 init();
132
133 if (x.empty() || y.empty() )
134 throw GnuplotException("vectors too small");
135
136 if (style == "")
137 this->set_style("lines");
138 else
139 this->set_style(style);
140
141 if (labelx == "")
142 this->set_xlabel("X");
143 else
144 this->set_xlabel(labelx);
145 if (labely == "")
146 this->set_ylabel("Y");
147 else
148 this->set_ylabel(labely);
149
150 this->plot_xy(x,y,title);
151
152 cout << "Press enter to continue" << endl;
153 while (getchar() != '\n'){}
154}
155
156// ----------------------------------------------------------------------------
157
158Gnuplot::Gnuplot(const string &title, const string &style,
159 const string &labelx, const string &labely,
160 const vector<double>& x)
161{
162 init();
163
164 if (x.empty() )
165 throw GnuplotException("vector too small");
166 if (!this->gnucmd)
167 throw GnuplotException("Could'nt open connection to gnuplot");
168
169 if (style == "")
170 this->set_style("lines");
171 else
172 this->set_style(style);
173
174 if (labelx == "")
175 this->set_xlabel("X");
176 else
177 this->set_xlabel(labelx);
178 if (labely == "")
179 this->set_ylabel("Y");
180 else
181 this->set_ylabel(labely);
182
183 this->plot_x(x,title);
184
185 cout << "Press enter to continue" << endl;
186 while (getchar() != '\n'){}
187}
188
189// ----------------------------------------------------------------------------
190
192{
193 if ( !((this->to_delete).empty()) )
194 {
195 for (size_t i = 0; i < this->to_delete.size(); i++)
196 remove(this->to_delete[i].c_str());
197 }
198
199 if (pclose(this->gnucmd) == -1)
200 cerr << "Problem closing communication to gnuplot" << endl;
201
202 return;
203}
204
205// ----------------------------------------------------------------------------
206
207bool Gnuplot::get_program_path(const string& pname)
208{
209 list<string> ls;
210 char *path;
211
212 path = getenv("PATH");
213 if (!path)
214 return false;
215
216 stringtok(ls, path, PATH_SEP);
217
218 for (list<string>::const_iterator i = ls.begin(); i != ls.end(); ++i)
219 {
220 string tmp = (*i) + "/" + pname;
221 if ( access(tmp.c_str(), ACCESS_OK) == 0 )
222 return true;
223 }
224
225 return false;
226}
227
228// ----------------------------------------------------------------------------
229
231{
232 if ( !(this->to_delete.empty()) )
233 {
234 for (size_t i = 0; i < this->to_delete.size(); i++)
235 remove(this->to_delete[i].c_str());
236 }
237 this->nplots = 0;
238 return;
239}
240
241// ----------------------------------------------------------------------------
242
243void Gnuplot::set_style(const string &stylestr)
244{
245 if (stylestr != "lines" &&
246 stylestr != "points" &&
247 stylestr != "linespoints" &&
248 stylestr != "impulses" &&
249 stylestr != "dots" &&
250 stylestr != "steps" &&
251 stylestr != "errorbars" &&
252 stylestr != "boxes" &&
253 stylestr != "boxerrorbars")
254 this->pstyle = string("points");
255 else
256 this->pstyle = stylestr;
257}
258
259// ----------------------------------------------------------------------------
260
261void Gnuplot::cmd(const char *_cmd, ...)
262{
263 va_list ap;
264 char local_cmd[GP_CMD_SIZE];
265
266 va_start(ap, _cmd);
267 vsprintf(local_cmd, _cmd, ap);
268 va_end(ap);
269
270 strcat(local_cmd,"\n");
271 fputs(local_cmd,this->gnucmd);
272 fflush(this->gnucmd);
273 return;
274}
275
276// ----------------------------------------------------------------------------
277
278void Gnuplot::set_ylabel(const string &label)
279{
280 ostringstream cmdstr;
281
282 cmdstr << "set xlabel \"" << label << "\"";
283 this->cmd(cmdstr.str().c_str());
284
285 return;
286}
287
288// ----------------------------------------------------------------------------
289
290void Gnuplot::set_xlabel(const string &label)
291{
292 ostringstream cmdstr;
293
294 cmdstr << "set xlabel \"" << label << "\"";
295 this->cmd(cmdstr.str().c_str());
296
297 return;
298}
299
300// ----------------------------------------------------------------------------
301
302// Plots a linear equation (where you supply the
303// slope and intercept)
304//
305void Gnuplot::plot_slope(double a, double b, const string &title)
306{
307 ostringstream stitle;
308 ostringstream cmdstr;
309
310 if (title == "")
311 stitle << "no title";
312 else
313 stitle << title;
314
315 if (this->nplots > 0)
316 cmdstr << "replot " << a << " * x + " << b << " title \"" << stitle.str() << "\" with " << pstyle;
317 else
318 cmdstr << "plot " << a << " * x + " << b << " title \"" << stitle.str() << "\" with " << pstyle;
319 this->cmd(cmdstr.str().c_str());
320 this->nplots++;
321 return;
322}
323
324// ----------------------------------------------------------------------------
325
326// Plot an equation which is supplied as a string
327//
328void Gnuplot::plot_equation(const string &equation, const string &title)
329{
330 string titlestr, plotstr;
331 ostringstream cmdstr;
332
333 if (title == "")
334 titlestr = "no title";
335 else
336 titlestr = title;
337
338 if (this->nplots > 0)
339 plotstr = "replot";
340 else
341 plotstr = "plot";
342
343 cmdstr << plotstr << " " << equation << " " << "title \"" << titlestr << "\" with " << this->pstyle;
344 this->cmd(cmdstr.str().c_str());
345 this->nplots++;
346
347 return;
348}
349
350// ----------------------------------------------------------------------------
351
352void Gnuplot::plot_x(const vector<double>& d, const string &title)
353{
354 ofstream tmp;
355 ostringstream cmdstr;
356#ifdef WIN32
357 char name[] = "gnuplotiXXXXXX";
358#else
359 char name[] = "/tmp/gnuplotiXXXXXX";
360#endif
361
362 if (this->to_delete.size() == GP_MAX_TMP_FILES - 1)
363 {
364 cerr << "Maximum number of temporary files reached (" << GP_MAX_TMP_FILES << "): cannot open more files" << endl;
365 return;
366 }
367
368 //
369 //open temporary files for output
370#ifdef WIN32
371 if ( _mktemp(name) == nullptr)
372#else
373 if ( mkstemp(name) == -1 )
374#endif
375 {
376 cerr << "Cannot create temporary file: exiting plot" << endl;
377 return;
378 }
379 tmp.open(name);
380 if (tmp.bad())
381 {
382 cerr << "Cannot create temorary file: exiting plot" << endl;
383 return;
384 }
385
386 //
387 // Save the temporary filename
388 //
389 this->to_delete.push_back(name);
390
391 //
392 // write the data to file
393 //
394 for (size_t i = 0; i < d.size(); i++)
395 tmp << d[i] << endl;
396 tmp.flush();
397 tmp.close();
398
399 //
400 // command to be sent to gnuplot
401 //
402 cmdstr << ( (this->nplots > 0) ? "replot " : "plot ");
403
404 if (title.empty())
405 cmdstr << "\"" << name << "\" with " << this->pstyle;
406 else
407 cmdstr << "\"" << name << "\" title \"" << title << "\" with "
408 << this->pstyle;
409
410 //
411 // Do the actual plot
412 //
413 this->cmd(cmdstr.str().c_str());
414 this->nplots++;
415
416 return;
417}
418
419// ----------------------------------------------------------------------------
420
421void Gnuplot::plot_xy(const vector<double>& x, const vector<double>& y, const string &title)
422{
423 ofstream tmp;
424 ostringstream cmdstr;
425#ifdef WIN32
426 char name[] = "gnuplotiXXXXXX";
427#else
428 char name[] = "/tmp/gnuplotiXXXXXX";
429#endif
430
431 // should raise an exception
432 if (x.size() != y.size())
433 return;
434
435 if ((this->to_delete).size() == GP_MAX_TMP_FILES - 1)
436 {
437 std::stringstream s;
438 s << "Maximum number of temporary files reached ("
439 << GP_MAX_TMP_FILES << "): cannot open more files" << endl;
440 throw GnuplotException( s.str() );
441 }
442
443 //open temporary files for output
444 //
445 if (MKTEMP_AND_CHECK_FAILED(name))
446 throw GnuplotException("Cannot create temporary file: exiting plot");
447
448 tmp.open(name);
449 if (tmp.bad())
450 throw GnuplotException("Cannot create temorary file: exiting plot");
451
452 // Save the temporary filename
453 //
454 this->to_delete.push_back(name);
455
456 // Write the data to file
457 //
458 size_t N = std::min(x.size(), y.size());
459 for (size_t i = 0; i < N; i++)
460 tmp << x[i] << " " << y[i] << endl;
461 tmp.flush();
462 tmp.close();
463
464 //
465 // command to be sent to gnuplot
466 //
467 if (this->nplots > 0)
468 cmdstr << "replot ";
469 else cmdstr << "plot ";
470 if (title == "")
471 cmdstr << "\"" << name << "\" with " << this->pstyle;
472 else
473 cmdstr << "\"" << name << "\" title \"" << title << "\" with " << this->pstyle;
474
475 //
476 // Do the actual plot
477 //
478 this->cmd(cmdstr.str().c_str());
479 this->nplots++;
480
481 return;
482}
483
484// ----------------------------------------------------------------------------
485
486void Gnuplot::init()
487{
488 if (!this->get_program_path(gnuplot_executable_))
489 {
490 this->valid = false;
491 throw GnuplotException("Can't find gnuplot in your PATH");
492 }
493
494 this->gnucmd = popen(gnuplot_executable_.c_str(),"w");
495 if (!this->gnucmd)
496 {
497 this->valid = false;
498 throw GnuplotException("Couldn't open connection to gnuplot");
499 }
500 this->nplots = 0;
501 this->valid = true;
502}
503
504// ============================================================================
Exception thrown by class Gnuplot.
Definition: Gnuplot.hh:75
void set_xlabel(const string &_xlabel)
set x axis label
Definition: Gnuplot.cc:290
void plot_slope(double _a, double _b, const string &_title)
Definition: Gnuplot.cc:305
void cmd(const char *_cmd,...)
Send a command to gnuplot (low-level function use by all plot functions.)
Definition: Gnuplot.cc:261
~Gnuplot()
Default constructor.
Definition: Gnuplot.cc:191
void plot_x(const vector< double > &_x, const string &_title)
Plot a single vector.
Definition: Gnuplot.cc:352
void plot_xy(const vector< double > &_x, const vector< double > &_y, const string &_title)
Plot x,y pairs.
Definition: Gnuplot.cc:421
Gnuplot()
Default constructor.
Definition: Gnuplot.cc:110
void reset_plot(void)
If multiple plots are present it will clear the plot area.
Definition: Gnuplot.cc:230
void set_style(const string &_style)
set line style
Definition: Gnuplot.cc:243
void plot_equation(const string &_equation, const string &_title)
Plot an equation supplied as a string.
Definition: Gnuplot.cc:328
void set_ylabel(const string &_ylabel)
set x axis label
Definition: Gnuplot.cc:278