Developer Documentation

Gnuplot.cc
1 //
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
58 using 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
68 template <typename Container>
69 void
70 stringtok (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
110 Gnuplot::Gnuplot(void)
111 {
112 init();
113 set_style("points");
114 }
115
116 // ----------------------------------------------------------------------------
117
118 Gnuplot::Gnuplot(const string &style)
119 {
120 init();
121 set_style(style);
122 }
123
124 // ----------------------------------------------------------------------------
125
126 Gnuplot::Gnuplot(const string &title,
127 const string &style,
128 const string &labelx, const string &labely,
129 vector<double> x, 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
158 Gnuplot::Gnuplot(const string &title, const string &style,
159 const string &labelx, const string &labely,
160 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
191 Gnuplot::~Gnuplot()
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
207 bool 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
230 void Gnuplot::reset_plot(void)
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
243 void 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
261 void 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
278 void 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
290 void 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 //
305 void 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 //
328 void 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
352 void Gnuplot::plot_x(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
421 void Gnuplot::plot_xy(vector<double> x, 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
486 void 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 // ============================================================================
Gnuplot::Gnuplot
Gnuplot()
Default constructor.
Definition: Gnuplot.cc:110
std
STL namespace.
Gnuplot::set_ylabel
void set_ylabel(const string &_ylabel)
set x axis label
Definition: Gnuplot.cc:278
Gnuplot::reset_plot
void reset_plot(void)
If multiple plots are present it will clear the plot area.
Definition: Gnuplot.cc:230
Gnuplot::set_style
void set_style(const string &_style)
set line style
Definition: Gnuplot.cc:243
Gnuplot::plot_xy
void plot_xy(vector< double > _x, vector< double > _y, const string &_title)
Plot x,y pairs.
Definition: Gnuplot.cc:421
Gnuplot::cmd
void cmd(const char *_cmd,...)
Send a command to gnuplot (low-level function use by all plot functions.)
Definition: Gnuplot.cc:261
Gnuplot::plot_equation
void plot_equation(const string &_equation, const string &_title)
Plot an equation supplied as a string.
Definition: Gnuplot.cc:328
GnuplotException
Exception thrown by class Gnuplot.
Definition: Gnuplot.hh:74
Gnuplot::plot_x
void plot_x(vector< double > _x, const string &_title)
Plot a single vector.
Definition: Gnuplot.cc:352
Gnuplot::plot_slope
void plot_slope(double _a, double _b, const string &_title)
Definition: Gnuplot.cc:305
Gnuplot::set_xlabel
void set_xlabel(const string &_xlabel)
set x axis label
Definition: Gnuplot.cc:290