//////////////////////////////////////////// // // A C++ interface to gnuplot. // // This is a direct translation from the C interface // written by N. Devillard (which is available from // http://ndevilla.free.fr/gnuplot/). // // As in the C interface this uses pipes and so wont // run on a system that does'nt have POSIX pipe // support // // Rajarshi Guha // // // 07/03/03 // //////////////////////////////////////////// #include "Gnuplot.hh" #include #ifdef WIN32 # include #else # include // X_OK # include // access # define PATH_MAXNAMESZ 4096 #endif #include #include #include #include #if defined(WIN32) # define pclose _pclose # define popen _popen # define access _access # define ACCESS_OK 0 # define PATH_SEP ";" # define MKTEMP_AND_CHECK_FAILED(name) (_mktemp(name) == NULL) #else # define ACCESS_OK X_OK # define PATH_SEP ":" # define MKTEMP_AND_CHECK_FAILED(name) (mkstemp(name) == -1) #endif #ifndef WIN32 #include #include #endif using namespace std; ///////////////////////////// // // A string tokenizer taken from // http://www.sunsite.ualberta.ca/ // Documentation/Gnu/libstdc++-2.90.8/html/21_strings/stringtok_std_h.txt // ///////////////////////////// template void stringtok (Container &container, string const &in, const char * const delimiters = " \t\n") { const string::size_type len = in.length(); string::size_type i = 0; while ( i < len ) { // eat leading whitespace i = in.find_first_not_of (delimiters, i); if (i == string::npos) return; // nothing left but white space // find the end of the token string::size_type j = in.find_first_of (delimiters, i); // push token if (j == string::npos) { container.push_back (in.substr(i)); return; } else container.push_back (in.substr(i, j-i)); // set up for next loop i = j + 1; } } // ---------------------------------------------------------------------------- #ifdef WIN32 std::string Gnuplot::gnuplot_executable_ = "pgnuplot.exe"; #else std::string Gnuplot::gnuplot_executable_ = "gnuplot"; #endif // ---------------------------------------------------------------------------- Gnuplot::Gnuplot(void) { init(); set_style("points"); } // ---------------------------------------------------------------------------- Gnuplot::Gnuplot(const string &style) { init(); set_style(style); } // ---------------------------------------------------------------------------- Gnuplot::Gnuplot(const string &title, const string &style, const string &labelx, const string &labely, vector x, vector y) { init(); if (x.size() == 0 || y.size() == 0) throw GnuplotException("vectors too small"); if (style == "") this->set_style("lines"); else this->set_style(style); if (labelx == "") this->set_xlabel("X"); else this->set_xlabel(labelx); if (labely == "") this->set_ylabel("Y"); else this->set_ylabel(labely); this->plot_xy(x,y,title); cout << "Press enter to continue" << endl; while (getchar() != '\n'){} } // ---------------------------------------------------------------------------- Gnuplot::Gnuplot(const string &title, const string &style, const string &labelx, const string &labely, vector x) { init(); if (x.size() == 0) throw GnuplotException("vector too small"); if (!this->gnucmd) throw GnuplotException("Could'nt open connection to gnuplot"); if (style == "") this->set_style("lines"); else this->set_style(style); if (labelx == "") this->set_xlabel("X"); else this->set_xlabel(labelx); if (labely == "") this->set_ylabel("Y"); else this->set_ylabel(labely); this->plot_x(x,title); cout << "Press enter to continue" << endl; while (getchar() != '\n'){} } // ---------------------------------------------------------------------------- Gnuplot::~Gnuplot() { if ((this->to_delete).size() > 0) { for (size_t i = 0; i < this->to_delete.size(); i++) remove(this->to_delete[i].c_str()); } if (pclose(this->gnucmd) == -1) cerr << "Problem closing communication to gnuplot" << endl; return; } // ---------------------------------------------------------------------------- bool Gnuplot::get_program_path(const string pname) { list ls; char *path; path = getenv("PATH"); if (!path) return false; stringtok(ls, path, PATH_SEP); for (list::const_iterator i = ls.begin(); i != ls.end(); ++i) { string tmp = (*i) + "/" + pname; if ( access(tmp.c_str(), ACCESS_OK) == 0 ) return true; } return false; } // ---------------------------------------------------------------------------- void Gnuplot::reset_plot(void) { if (this->to_delete.size() > 0) { for (size_t i = 0; i < this->to_delete.size(); i++) remove(this->to_delete[i].c_str()); } this->nplots = 0; return; } // ---------------------------------------------------------------------------- void Gnuplot::set_style(const string &stylestr) { if (stylestr != "lines" && stylestr != "points" && stylestr != "linespoints" && stylestr != "impulses" && stylestr != "dots" && stylestr != "steps" && stylestr != "errorbars" && stylestr != "boxes" && stylestr != "boxerrorbars") this->pstyle = string("points"); else this->pstyle = stylestr; } // ---------------------------------------------------------------------------- void Gnuplot::cmd(const char *_cmd, ...) { va_list ap; char local_cmd[GP_CMD_SIZE]; va_start(ap, _cmd); vsprintf(local_cmd, _cmd, ap); va_end(ap); strcat(local_cmd,"\n"); fputs(local_cmd,this->gnucmd); fflush(this->gnucmd); return; } // ---------------------------------------------------------------------------- void Gnuplot::set_ylabel(const string &label) { ostringstream cmdstr; cmdstr << "set xlabel \"" << label << "\""; this->cmd(cmdstr.str().c_str()); return; } // ---------------------------------------------------------------------------- void Gnuplot::set_xlabel(const string &label) { ostringstream cmdstr; cmdstr << "set xlabel \"" << label << "\""; this->cmd(cmdstr.str().c_str()); return; } // ---------------------------------------------------------------------------- // Plots a linear equation (where you supply the // slope and intercept) // void Gnuplot::plot_slope(double a, double b, const string &title) { ostringstream stitle; ostringstream cmdstr; if (title == "") stitle << "no title"; else stitle << title; if (this->nplots > 0) cmdstr << "replot " << a << " * x + " << b << " title \"" << stitle.str() << "\" with " << pstyle; else cmdstr << "plot " << a << " * x + " << b << " title \"" << stitle.str() << "\" with " << pstyle; this->cmd(cmdstr.str().c_str()); this->nplots++; return; } // ---------------------------------------------------------------------------- // Plot an equation which is supplied as a string // void Gnuplot::plot_equation(const string &equation, const string &title) { string titlestr, plotstr; ostringstream cmdstr; if (title == "") titlestr = "no title"; else titlestr = title; if (this->nplots > 0) plotstr = "replot"; else plotstr = "plot"; cmdstr << plotstr << " " << equation << " " << "title \"" << titlestr << "\" with " << this->pstyle; this->cmd(cmdstr.str().c_str()); this->nplots++; return; } // ---------------------------------------------------------------------------- void Gnuplot::plot_x(vector d, const string &title) { ofstream tmp; ostringstream cmdstr; #ifdef WIN32 char name[] = "gnuplotiXXXXXX"; #else char name[] = "/tmp/gnuplotiXXXXXX"; #endif if (this->to_delete.size() == GP_MAX_TMP_FILES - 1) { cerr << "Maximum number of temporary files reached (" << GP_MAX_TMP_FILES << "): cannot open more files" << endl; return; } // //open temporary files for output #ifdef WIN32 if ( _mktemp(name) == NULL) #else if ( mkstemp(name) == -1 ) #endif { cerr << "Cannot create temporary file: exiting plot" << endl; return; } tmp.open(name); if (tmp.bad()) { cerr << "Cannot create temorary file: exiting plot" << endl; return; } // // Save the temporary filename // this->to_delete.push_back(name); // // write the data to file // for (size_t i = 0; i < d.size(); i++) tmp << d[i] << endl; tmp.flush(); tmp.close(); // // command to be sent to gnuplot // cmdstr << ( (this->nplots > 0) ? "replot " : "plot "); if (title.empty()) cmdstr << "\"" << name << "\" with " << this->pstyle; else cmdstr << "\"" << name << "\" title \"" << title << "\" with " << this->pstyle; // // Do the actual plot // this->cmd(cmdstr.str().c_str()); this->nplots++; return; } // ---------------------------------------------------------------------------- void Gnuplot::plot_xy(vector x, vector y, const string &title) { ofstream tmp; ostringstream cmdstr; #ifdef WIN32 char name[] = "gnuplotiXXXXXX"; #else char name[] = "/tmp/gnuplotiXXXXXX"; #endif // should raise an exception if (x.size() != y.size()) return; if ((this->to_delete).size() == GP_MAX_TMP_FILES - 1) { std::stringstream s; s << "Maximum number of temporary files reached (" << GP_MAX_TMP_FILES << "): cannot open more files" << endl; throw GnuplotException( s.str() ); } //open temporary files for output // if (MKTEMP_AND_CHECK_FAILED(name)) throw GnuplotException("Cannot create temporary file: exiting plot"); tmp.open(name); if (tmp.bad()) throw GnuplotException("Cannot create temorary file: exiting plot"); // Save the temporary filename // this->to_delete.push_back(name); // Write the data to file // size_t N = std::min(x.size(), y.size()); for (size_t i = 0; i < N; i++) tmp << x[i] << " " << y[i] << endl; tmp.flush(); tmp.close(); // // command to be sent to gnuplot // if (this->nplots > 0) cmdstr << "replot "; else cmdstr << "plot "; if (title == "") cmdstr << "\"" << name << "\" with " << this->pstyle; else cmdstr << "\"" << name << "\" title \"" << title << "\" with " << this->pstyle; // // Do the actual plot // this->cmd(cmdstr.str().c_str()); this->nplots++; return; } // ---------------------------------------------------------------------------- void Gnuplot::init() { if (!this->get_program_path(gnuplot_executable_)) { this->valid = false; throw GnuplotException("Can't find gnuplot in your PATH"); } this->gnucmd = popen(gnuplot_executable_.c_str(),"w"); if (!this->gnucmd) { this->valid = false; throw GnuplotException("Couldn't open connection to gnuplot"); } this->nplots = 0; this->valid = true; } // ============================================================================