00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 #ifndef GNUPLOT_IOSTREAM_H
00024 #define GNUPLOT_IOSTREAM_H
00025 
00026 
00027 #include <stdio.h>
00028 #ifdef GNUPLOT_ENABLE_PTY
00029 #include <termios.h>
00030 #include <unistd.h>
00031 #include <pty.h>
00032 #endif // GNUPLOT_ENABLE_PTY
00033 
00034 
00035 #include <fstream>
00036 #include <iostream>
00037 #include <sstream>
00038 #include <stdexcept>
00039 #include <string>
00040 #include <utility>
00041 #include <iomanip>
00042 #include <vector>
00043 
00044 
00045 #include "boost/iostreams/device/file_descriptor.hpp"
00046 #include "boost/iostreams/stream.hpp"
00047 #include "boost/version.hpp"
00048 #include "boost/utility.hpp"
00049 #ifdef GNUPLOT_ENABLE_BLITZ
00050 #include "blitz/array.h"
00051 #endif
00052 
00053 
00054 #if BOOST_VERSION >= 104600
00055 #define GNUPLOT_USE_TMPFILE
00056 #include "boost/filesystem.hpp"
00057 #endif // BOOST_VERSION
00058 
00059 
00060 #ifdef WIN32
00061 #define PCLOSE _pclose
00062 #define POPEN  _popen
00063 #define FILENO _fileno
00064 #else
00065 #define PCLOSE pclose
00066 #define POPEN  popen
00067 #define FILENO fileno
00068 #endif
00069 
00071 
00072 #ifdef GNUPLOT_USE_TMPFILE
00073 
00074 class GnuplotTmpfile {
00075 public:
00076         GnuplotTmpfile() :
00077                 file(boost::filesystem::unique_path(
00078                         boost::filesystem::temp_directory_path() /
00079                         "tmp-gnuplot-%%%%-%%%%-%%%%-%%%%"))
00080         { }
00081 
00082 private:
00083         
00084         GnuplotTmpfile(const GnuplotTmpfile &);
00085         const GnuplotTmpfile& operator=(const GnuplotTmpfile &);
00086 
00087 public:
00088         ~GnuplotTmpfile() {
00089                 
00090                 try {
00091                         remove(file);
00092                 } catch(const std::exception &e) {
00093                         std::cerr << "Failed to remove temporary file " << file << std::endl;
00094                 }
00095         }
00096 
00097 public:
00098         boost::filesystem::path file;
00099 };
00100 #endif // GNUPLOT_USE_TMPFILE
00101 
00103 
00104 
00105 class GnuplotFeedback {
00106 public:
00107         GnuplotFeedback() { }
00108         virtual ~GnuplotFeedback() { }
00109         virtual std::string filename() const = 0;
00110         virtual FILE *handle() const = 0;
00111 
00112 private:
00113         
00114         GnuplotFeedback(const GnuplotFeedback &);
00115         const GnuplotFeedback& operator=(const GnuplotFeedback &);
00116 };
00117 
00118 #ifdef GNUPLOT_ENABLE_PTY
00119 #define GNUPLOT_ENABLE_FEEDBACK
00120 class GnuplotFeedbackPty : public GnuplotFeedback {
00121 public:
00122         explicit GnuplotFeedbackPty(bool debug_messages) :
00123                 pty_fn(),
00124                 pty_fh(NULL),
00125                 master_fd(-1),
00126                 slave_fd(-1)
00127         {
00128         
00129                 if(0 > openpty(&master_fd, &slave_fd, NULL, NULL, NULL)) {
00130                         perror("openpty");
00131                         throw std::runtime_error("openpty failed");
00132                 }
00133                 char pty_fn_buf[1024];
00134                 if(ttyname_r(slave_fd, pty_fn_buf, 1024)) {
00135                         perror("ttyname_r");
00136                         throw std::runtime_error("ttyname failed");
00137                 }
00138                 pty_fn = std::string(pty_fn_buf);
00139                 if(debug_messages) {
00140                         std::cerr << "feedback_fn=" << pty_fn << std::endl;
00141                 }
00142 
00143                 
00144                 struct termios tios;
00145                 if(tcgetattr(slave_fd, &tios) < 0) {
00146                         perror("tcgetattr");
00147                         throw std::runtime_error("tcgetattr failed");
00148                 }
00149                 tios.c_lflag &= ~(ECHO | ECHONL);
00150                 if(tcsetattr(slave_fd, TCSAFLUSH, &tios) < 0) {
00151                         perror("tcsetattr");
00152                         throw std::runtime_error("tcsetattr failed");
00153                 }
00154 
00155                 pty_fh = fdopen(master_fd, "r");
00156                 if(!pty_fh) {
00157                         throw std::runtime_error("fdopen failed");
00158                 }
00159         }
00160 
00161 private:
00162         
00163         GnuplotFeedbackPty(const GnuplotFeedbackPty &);
00164         const GnuplotFeedbackPty& operator=(const GnuplotFeedbackPty &);
00165 
00166 public:
00167         ~GnuplotFeedbackPty() {
00168                 if(pty_fh) fclose(pty_fh);
00169                 if(master_fd > 0) ::close(master_fd);
00170                 if(slave_fd  > 0) ::close(slave_fd);
00171         }
00172 
00173         std::string filename() const {
00174                 return pty_fn;
00175         }
00176 
00177         FILE *handle() const {
00178                 return pty_fh;
00179         }
00180 
00181 private:
00182         std::string pty_fn;
00183         FILE *pty_fh;
00184         int master_fd, slave_fd;
00185 };
00186 
00188 
00189 
00190 
00191 
00192 
00193 
00194 
00195 
00196 
00197 
00198 
00199 
00200 
00201 
00202 
00203 
00204 
00205 
00206 
00207 
00208 
00209 
00210 
00211 
00212 
00213 
00214 
00215 
00216 
00217 
00218 
00219 
00220 
00221 
00222 
00223 #endif // GNUPLOT_ENABLE_PTY, GNUPLOT_USE_TMPFILE
00224 
00226 
00227 
00228 class GnuplotWriter {
00229 public:
00230         explicit GnuplotWriter(std::ostream *_stream, bool _send_e=true) :
00231                 stream(_stream),
00232                 send_e(_send_e)
00233         { }
00234 
00235 private:
00236         template <class T>
00237         void sendEntry(T v) {
00238                 *stream << v << " ";
00239         }
00240 
00241         template <class T, class U>
00242         void sendEntry(std::pair<T, U> v) {
00243                 sendEntry(v.first, v.second);
00244         }
00245 
00246         template <class T, class U>
00247         void sendEntry(T t, U u) {
00248                 sendEntry(t);
00249                 sendEntry(u);
00250         }
00251 
00252         std::string formatCode(   float *) { return "%float"; }
00253         std::string formatCode(  double *) { return "%double"; }
00254         std::string formatCode(  int8_t *) { return "%int8"; }
00255         std::string formatCode( uint8_t *) { return "%uint8"; }
00256         std::string formatCode( int16_t *) { return "%int16"; }
00257         std::string formatCode(uint16_t *) { return "%uint16"; }
00258         std::string formatCode( int32_t *) { return "%int32"; }
00259         std::string formatCode(uint32_t *) { return "%uint32"; }
00260         std::string formatCode( int64_t *) { return "%int64"; }
00261         std::string formatCode(uint64_t *) { return "%uint64"; }
00262 
00263 public:
00264         
00265         template <class T>
00266         void sendIter(T p, T last) {
00267                 while(p != last) {
00268                         sendEntry(*p);
00269                         *stream << "\n";
00270                         ++p;
00271                 }
00272                 if(send_e) {
00273                         *stream << "e" << std::endl; 
00274                 }
00275         }
00276 
00277         
00278         template <class T, class U>
00279         void sendIterPair(T x, T x_last, U y, U y_last) {
00280                 while(x != x_last && y != y_last) {
00281                         sendEntry(*x, *y);
00282                         *stream << "\n";
00283                         ++x;
00284                         ++y;
00285                 }
00286                 
00287                 assert(x==x_last && y==y_last);
00288                 if(send_e) {
00289                         *stream << "e" << std::endl; 
00290                 }
00291         }
00292 
00293         
00294         
00295         template <class T>
00296         void send(T arr) {
00297                 sendIter(arr.begin(), arr.end());
00298         }
00299 
00300         template <class T>
00301         void sendBinary(const std::vector<T> &arr) {
00302                 stream->write(reinterpret_cast<const char *>(&arr[0]), arr.size() * sizeof(T));
00303         }
00304 
00305         template <class T>
00306         std::string binfmt(const std::vector<T> &arr) {
00307                 std::ostringstream tmp;
00308                 tmp << " format='" << formatCode((T*)NULL) << "'";
00309                 tmp << " array=(" << arr.size() << ")";
00310                 tmp << " ";
00311                 return tmp.str();
00312         }
00313 
00314         
00315         template <class T>
00316         void send(const std::vector<std::vector <T> > &vectors) {
00317                 
00318                 assert(vectors.size() > 0);
00319                 for(size_t i=1; i<vectors.size(); i++) {
00320                         assert(vectors[i].size() == vectors[i-1].size());
00321                 }
00322 
00323                 for(size_t i=0; i<vectors[0].size(); i++) {
00324                         for(size_t j=0; j<vectors.size(); j++) {
00325                                 sendEntry(vectors[j][i]);
00326                         }
00327                         *stream << "\n";
00328                 }
00329                 if(send_e) {
00330                         *stream << "e" << std::endl; 
00331                 }
00332         }
00333 
00334         template <class T>
00335         std::string binfmt(const std::vector<std::vector<T> > &arr) {
00336                 assert(arr.size() > 0);
00337                 std::ostringstream tmp;
00338                 tmp << " format='";
00339                 for(size_t i=0; i<arr.size(); i++) {
00340                         tmp << formatCode((T*)NULL);
00341                 }
00342                 tmp << "' array=(" << arr[0].size() << ")";
00343                 tmp << " ";
00344                 return tmp.str();
00345         }
00346 
00347         
00348         template <class T>
00349         void sendBinary(const std::vector<std::vector <T> > &vectors) {
00350                 
00351                 assert(vectors.size() > 0);
00352                 for(size_t i=1; i<vectors.size(); i++) {
00353                         assert(vectors[i].size() == vectors[i-1].size());
00354                 }
00355 
00356                 for(size_t i=0; i<vectors[0].size(); i++) {
00357                         for(size_t j=0; j<vectors.size(); j++) {
00358                                 const T &val = vectors[j][i];
00359                                 stream->write(reinterpret_cast<const char *>(&val), sizeof(T));
00360                         }
00361                 }
00362         }
00363 
00364 #ifdef GNUPLOT_ENABLE_BLITZ
00365         
00366         template <class T>
00367         void send(const blitz::Array<T, 2> &a) {
00368                 for(int i=a.lbound(0); i<=a.ubound(0); i++) {
00369                         for(int j=a.lbound(1); j<=a.ubound(1); j++) {
00370                                 sendEntry(a(i, j));
00371                                 *stream << "\n";
00372                         }
00373                         *stream << "\n"; 
00374                 }
00375                 if(send_e) {
00376                         *stream << "e" << std::endl; 
00377                 }
00378         }
00379 
00380         template <class T, int d>
00381         void sendBinary(const blitz::Array<T, d> &arr) {
00382                 stream->write(reinterpret_cast<const char *>(arr.data()), arr.size() * sizeof(T));
00383         }
00384 
00385         template <class T>
00386         std::string binfmt(const blitz::Array<T, 2> &arr) {
00387                 std::ostringstream tmp;
00388                 tmp << " format='" << formatCode((T*)NULL) << "'";
00389                 tmp << " array=(" << arr.extent(0) << "," << arr.extent(1) << ")";
00390                 if(arr.isMajorRank(0)) tmp << "scan=yx"; 
00391                 tmp << " ";
00392                 return tmp.str();
00393         }
00394 
00395 private:
00396         template <class T, int N>
00397         void sendEntry(blitz::TinyVector<T, N> v) {
00398                 for(int i=0; i<N; i++) {
00399                         sendEntry(v[i]);
00400                 }
00401         }
00402 
00403         template <class T, int N>
00404         std::string formatCode(blitz::TinyVector<T, N> *) {
00405                 std::ostringstream tmp;
00406                 for(int i=0; i<N; i++) {
00407                         tmp << formatCode((T*)NULL);
00408                 }
00409                 return tmp.str();
00410         }
00411 #endif // GNUPLOT_ENABLE_BLITZ
00412 
00413 private:
00414         std::ostream *stream;
00415         bool send_e;
00416 };
00417 
00419 
00420 class Gnuplot : public boost::iostreams::stream<
00421         boost::iostreams::file_descriptor_sink>
00422 {
00423 public:
00424         explicit Gnuplot(const std::string &cmd = "gnuplot") :
00425                 boost::iostreams::stream<boost::iostreams::file_descriptor_sink>(
00426                         FILENO(pout = POPEN(cmd.c_str(), "w")),
00427                         boost::iostreams::never_close_handle
00428                 ),
00429                 pout(pout), 
00430                 is_pipe(true),
00431                 feedback(NULL),
00432                 writer(this),
00433                 tmp_files(),
00434                 debug_messages(false)
00435         {
00436                 *this << std::scientific << std::setprecision(18);  
00437         }
00438 
00439         explicit Gnuplot(FILE *fh) :
00440                 boost::iostreams::stream<boost::iostreams::file_descriptor_sink>(
00441                         FILENO(pout = fh),
00442                         boost::iostreams::never_close_handle
00443                 ),
00444                 pout(pout), 
00445                 is_pipe(false),
00446                 feedback(NULL),
00447                 writer(this),
00448                 tmp_files(),
00449                 debug_messages(false)
00450         {
00451                 *this << std::scientific << std::setprecision(18);  
00452         }
00453 
00454 private:
00455         
00456         Gnuplot(const Gnuplot &);
00457         const Gnuplot& operator=(const Gnuplot &);
00458 
00459 public:
00460         ~Gnuplot() {
00461                 if(debug_messages) {
00462                         std::cerr << "ending gnuplot session" << std::endl;
00463                 }
00464 
00465                 
00466                 
00467                 
00468                 *this << std::flush;
00469                 fflush(pout);
00470                 
00471                 
00472 
00473                 if(is_pipe) {
00474                         if(PCLOSE(pout)) {
00475                                 std::cerr << "pclose returned error" << std::endl;
00476                         }
00477                 } else {
00478                         if(fclose(pout)) {
00479                                 std::cerr << "fclose returned error" << std::endl;
00480                         }
00481                 }
00482 
00483                 if(feedback) delete(feedback);
00484         }
00485 
00486 public:
00487         
00488         
00489 
00490         template <class T1>
00491         Gnuplot &send(T1 arg1) {
00492                 writer.send(arg1);
00493                 return *this;
00494         }
00495 
00496         
00497         
00498         
00499         template <typename T, std::size_t N>
00500         Gnuplot &send(T (&arr)[N]) {
00501                 writer.sendIter(arr, arr+N);
00502                 return *this;
00503         }
00504 
00505         template <class T1, class T2>
00506         Gnuplot &send(T1 arg1, T2 arg2) {
00507                 writer.sendIter(arg1, arg2);
00508                 return *this;
00509         }
00510 
00511         template <class T1, class T2, class T3, class T4>
00512         Gnuplot &send(T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
00513                 writer.sendIterPair(arg1, arg2, arg3, arg4);
00514                 return *this;
00515         }
00516 
00517         template <class T1>
00518         Gnuplot &sendBinary(T1 arg1) {
00519                 writer.sendBinary(arg1);
00520                 return *this;
00521         }
00522 
00523         template <class T>
00524         std::string binfmt(T arg) {
00525                 return writer.binfmt(arg);
00526         }
00527 
00528 private:
00529         std::string make_tmpfile() {
00530 #ifdef GNUPLOT_USE_TMPFILE
00531                 boost::shared_ptr<GnuplotTmpfile> tmp_file(new GnuplotTmpfile());
00532                 
00533                 
00534                 tmp_files.push_back(tmp_file);
00535                 return tmp_file->file.string();
00536 #else
00537                 throw(std::logic_error("no filename given and temporary files not enabled"));
00538 #endif // GNUPLOT_USE_TMPFILE
00539         }
00540 
00541 public:
00542         
00543         template <class T1>
00544         std::string file(T1 arg1, std::string filename="") {
00545                 if(filename.empty()) filename = make_tmpfile();
00546                 std::fstream tmp_stream(filename.c_str(), std::fstream::out);
00547                 GnuplotWriter tmp_writer(&tmp_stream, false);
00548                 tmp_writer.send(arg1);
00549                 tmp_stream.close();
00550 
00551                 std::ostringstream cmdline;
00552                 
00553                 cmdline << " '" << filename << "' ";
00554                 return cmdline.str();
00555         }
00556 
00557         
00558         template <class T1>
00559         std::string binaryFile(T1 arg1, std::string filename="") {
00560                 if(filename.empty()) filename = make_tmpfile();
00561                 std::fstream tmp_stream(filename.c_str(), std::fstream::out | std::fstream::binary);
00562                 GnuplotWriter tmp_writer(&tmp_stream);
00563                 tmp_writer.sendBinary(arg1);
00564                 tmp_stream.close();
00565 
00566                 std::ostringstream cmdline;
00567                 
00568                 cmdline << " '" << filename << "' binary" << binfmt(arg1);
00569                 return cmdline.str();
00570         }
00571 
00572         void clearTmpfiles() {
00573                 
00574                 tmp_files.clear();
00575         }
00576 
00577 #ifdef GNUPLOT_ENABLE_FEEDBACK
00578         
00579         
00580         
00581         void getMouse(
00582                 double &mx, double &my, int &mb,
00583                 std::string msg="Click Mouse!"
00584         ) {
00585                 allocFeedback();
00586 
00587                 *this << "set mouse" << std::endl;
00588                 *this << "pause mouse \"" << msg << "\\n\"" << std::endl;
00589                 *this << "if (exists(\"MOUSE_X\")) print MOUSE_X, MOUSE_Y, MOUSE_BUTTON; else print 0, 0, -1;" << std::endl;
00590                 if(debug_messages) {
00591                         std::cerr << "begin scanf" << std::endl;
00592                 }
00593                 if(3 != fscanf(feedback->handle(), "%50lf %50lf %50d", &mx, &my, &mb)) {
00594                         throw std::runtime_error("could not parse reply");
00595                 }
00596                 if(debug_messages) {
00597                         std::cerr << "end scanf" << std::endl;
00598                 }
00599         }
00600 
00601         void allocFeedback() {
00602                 if(!feedback) {
00603 #ifdef GNUPLOT_ENABLE_PTY
00604                         feedback = new GnuplotFeedbackPty(debug_messages);
00605 
00607 
00608 #endif
00609                         *this << "set print \"" << feedback->filename() << "\"" << std::endl;
00610                 }
00611         }
00612 #endif // GNUPLOT_ENABLE_FEEDBACK
00613 
00614 private:
00615         FILE *pout;
00616         bool is_pipe;
00617         GnuplotFeedback *feedback;
00618         GnuplotWriter writer;
00619 #ifdef GNUPLOT_USE_TMPFILE
00620         std::vector<boost::shared_ptr<GnuplotTmpfile> > tmp_files;
00621 #else
00622         
00623         std::vector<int> tmp_files;
00624 #endif // GNUPLOT_USE_TMPFILE
00625 
00626 public:
00627         bool debug_messages;
00628 };
00629 
00630 #endif // GNUPLOT_IOSTREAM_H