C++からGNUPLOT[1]をつかってグラフを表示したり,それをepsで保存したり.
g++とGNUPLOTが必要.
とにかくGNUPLOTを起動し,それにコマンドを渡せばよい[2][3][4].これには
の2つの方法が考えられるが,前者は
後者は
と,それぞれ一長一短がある.
いずれにせよ
というように,初期化と後かたづけ部分が違うだけなので,
共通の基本クラスからパイプ版とファイル版を派生させる
という方向で実装する.
ソースは以下の通り.もちろん宣言と定義をわけてもいいが,大した量でもないのですべてインライン関数にしてしまった方が楽かもしれない.
#ifndef GNUPLOT_H
#define GNUPLOT_H
#include <cstdio>
#include <string>
#include <cstdarg>
class Gnuplot_Base
{
protected:
std::string exec;
std::string cmdline;
FILE * fp;
public:
explicit Gnuplot_Base(const char * execname=NULL) : fp(NULL)
{
if(execname)
{
exec = execname;
}
else
{
exec = "gnuplot";
}
}
virtual ~Gnuplot_Base()
{
end();
}
enum { GNUPLOT_OK = 0,
GNUPLOT_NG,
GNUPLOT_ALREADY_OPEN,
GNUPLOT_NOT_OPEN,
GNUPLOT_CANNOT_EXEC
};
virtual int begin(const char * cmd_opt=NULL, const char * reserved=NULL) = \
0;
virtual int end() {return 0;}
int flush()
{
if( fp ) ::fflush(fp);
return GNUPLOT_OK;
}
int begin_data()
{
if( ! fp ) return GNUPLOT_NOT_OPEN;
return flush();
}
int end_data()
{
if( ! fp ) return GNUPLOT_NOT_OPEN;
::fprintf(fp, "e\n");
return flush();
}
int command(const char * cmd, ...)
{
if(!fp) return GNUPLOT_NOT_OPEN;
if(!cmd) return GNUPLOT_OK;
{ va_list ap; va_start(ap, cmd); ::vfprintf(fp, cmd, ap); va_end(ap); }
return GNUPLOT_OK;
}
int commandln(const char * cmd, ...)
{
if(!fp) return GNUPLOT_NOT_OPEN;
if(!cmd) return GNUPLOT_OK;
{ va_list ap; va_start(ap, cmd); ::vfprintf(fp, cmd, ap); va_end(ap); }
::fprintf(fp, "\n");
return GNUPLOT_OK;
}
};
class Gnuplot_Pipe : public Gnuplot_Base
{
public:
virtual int begin(const char * cmd_opt=NULL, const char * reserved=NULL)
{
if(fp) return GNUPLOT_ALREADY_OPEN;
cmdline = exec;
if(cmd_opt)
{
cmdline += " ";
cmdline += cmd_opt;
}
fp = ::popen( cmdline.c_str(), "w" );
if( !fp ) return GNUPLOT_CANNOT_EXEC;
return GNUPLOT_OK;
}
virtual int end()
{
if( ! fp ) return GNUPLOT_OK;
::fflush(fp);
::pclose(fp);
fp = NULL;
return GNUPLOT_OK;
}
};
class Gnuplot_Tmpfile : public Gnuplot_Base
{
protected:
std::string tmpfilename;
bool remove_file;
public:
virtual int begin(const char * cmd_opt=NULL, const char * filename=NULL)
{
if(fp) return GNUPLOT_ALREADY_OPEN;
cmdline = exec;
if(cmd_opt)
{
cmdline += " ";
cmdline += cmd_opt;
}
if(filename)
{
fp = ::fopen(filename, "w");
tmpfilename = filename;
remove_file = false;
}
else
{
char buf[] = "/tmp/gnuplot_XXXXXX";
int fd = ::mkstemp(buf);
if( fd == -1 ) return GNUPLOT_NG;
fp = ::fdopen( fd, "w" );
tmpfilename = buf;
remove_file = true;
}
if( !fp )
{
tmpfilename.clear();
return GNUPLOT_CANNOT_EXEC;
}
return GNUPLOT_OK;
}
virtual int end()
{
if( ! fp ) return GNUPLOT_OK;
::fflush(fp);
::fclose(fp);
fp = NULL;
::system( (cmdline + " " + tmpfilename).c_str() );
if( remove_file ) ::remove( tmpfilename.c_str() );
return GNUPLOT_OK;
}
};
// use pipe version as default
typedef Gnuplot_Pipe Gnuplot;
#endif //GNUPLOT_H
ポイントは,
など.
GNUPLOTのバージョンが3.8とかでマウスパッチが当たっていれば,以下のようにsplotしたときに,マウスでぐるぐる回転できるはず.なおパイプ版を使うと,回転してくれない.
#include "gnuplot.h"
int main(int argc, char *argv[])
{
Gnuplot_Tmpfile gp;
gp.begin();
gp.commandln("splot '-' w l");
gp.begin_data();
for (double x = -10; x <= 10; x += 1)
{
for (double y = -10; y <= 10; y += 1)
{
gp.commandln("%f %f %f", x, y, x*x-y*y);
}
gp.commandln("");
}
gp.end_data();
gp.commandln("pause -1");
gp.end();
return 0;
}
なお,
gp.begin();
を
gp.begin("", "a.gp");
のようにすると,一時ファイルではなく指定された"a.gp"を使い,end()で削除も行わない.
[2]にあるサンプルを移植したもの.アニメーションする.
#include "gnuplot.h"
#include <cmath>
#include <unistd.h>
int main(int argc, char *argv[])
{
Gnuplot gp;
gp.begin();
gp.commandln("set noautoscale x");
gp.commandln("set noautoscale y");
gp.commandln("set xrange [-2*pi:2*pi]");
gp.commandln("set yrange [-2:2]");
gp.commandln("plot sin(x)+sin(x*0.9)");
for (int i = 100; i > 0; i--)
{
double b = M_PI * i / 5;
double a = M_PI * 10 - b;
b += M_PI * 10;
usleep(10000);
gp.commandln("set xrange [%5.2f:%5.2f]", a, b);
gp.commandln("replot");
}
gp.commandln("pause -1");
gp.end();
return 0;
}
| [1] | Thomas Williams, Colin Kelley. GNUPLOT. http://www.gnuplot.info/ |
| [2] | 山内千里. GPTCALL version 0.32. http://phe.phyas.aichi-edu.ac.jp/~cyamauch/gptcall.html |
| [3] | 高橋隆史. CのプログラムからGNUPLOTを動かす方法. http://tortoise1.math.ryukoku.ac.jp/~takataka/gnuplot/fromC.html |
| [4] | 松田七美男. C言語からの呼び出し. http://ayapin.film.s.dendai.ac.jp/~matuda/Gnuplot/Tips/tips.html#callfrom |