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) == NULL)
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 
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 
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) == NULL)
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()
Default constructor.
Definition: Gnuplot.cc:110
void plot_slope(double _a, double _b, const string &_title)
Definition: Gnuplot.cc:305
void reset_plot(void)
If multiple plots are present it will clear the plot area.
Definition: Gnuplot.cc:230
void plot_x(vector< double > _x, const string &_title)
Plot a single vector.
Definition: Gnuplot.cc:352
void plot_equation(const string &_equation, const string &_title)
Plot an equation supplied as a string.
Definition: Gnuplot.cc:328
void cmd(const char *_cmd,...)
Send a command to gnuplot (low-level function use by all plot functions.)
Definition: Gnuplot.cc:261
void set_ylabel(const string &_ylabel)
set x axis label
Definition: Gnuplot.cc:278
void set_xlabel(const string &_xlabel)
set x axis label
Definition: Gnuplot.cc:290
Exception thrown by class Gnuplot.
Definition: Gnuplot.hh:74
STL namespace.
void plot_xy(vector< double > _x, vector< double > _y, const string &_title)
Plot x,y pairs.
Definition: Gnuplot.cc:421
void set_style(const string &_style)
set line style
Definition: Gnuplot.cc:243