ページ

2013年10月9日水曜日

gnuplot animation in C

 とある需要で,c言語でreal time animationを作る事になった.
gnuplotで描画させるのが一番簡単だろうなーと思ったので,基本的な操作方法をまとめる.
gnuplotを立ち上げて
gnuplot> plot "-"
と打つとデータの入力待ちになるので,
gnuplot> plot[-1:1][-1:1] "-" w p ps 7 pt 7
input data ('e' ends) > 0.0 0.0 
input data ('e' ends) > e
とかしてやると,データを手動で入力することができる.gnuplotを日常的に使っている人ならば直感的に,
gnuplot> plot[-1:1][-1:1] "-" w p ps 7 pt 7,"-" w p ps 7 pt 7
input data ('e' ends) > 0.5 0.0
input data ('e' ends) > e
input data ('e' ends) > -0.5 0.0
input data ('e' ends) > e
と二つ以上のデータ点を入力することができることに気がつくだろう.
結果は上図の様になる.つまり,cからgnuplotに上記のような命令でデータを入力してやれば,real time にデータを更新しながら描画することが可能になる.
(note:multiplotを利用するとデータ更新毎に再描画してしまうので,ちらつきの原因となる)
さて,ここからが本題.C言語からgnuplotに命令するため,ここではpopen関数をつかう.(多分やり方は色々有るはず)
FILE *gp;
gp = popen("gnuplot", "w");
でgnuplotにパイプを通すことができる.もちろんgnuplotのPATHが通ってなければならない.絶対PATHでも構わない. もし描画終了後にgnplotのウィンドウ画面が閉じるのが嫌であればgnuplotの起動オプション
gp = popen("gnuplot -persist", "w"); 
を付けてやれば良い.

以下簡単なサンプル
#include <stdio.h>
#include <stdlib.h>
#include <tmath.h>
#include <unistd.h>

void gnuplot_config(FILE *gp);
void plot2d(FILE *gp,const int num,const double *x,const double *y);
void move(const int num, double const t, double *x, double *y);
int main(void){
    FILE *gp;

    //gp = popen("gnuplot -persist", "w");    /*プログラムが終了後GnuplotのWindowを閉じない*/
    gp = popen("gnuplot", "w");               /*プログラムが終了後GnuplotのWindowを閉じる*/
    gnuplot_config(gp);  /* gnuplot configuration */


    int i;          // loop variable
    int step=100;
    int sample_num=10;
    double t;
    double dt=0.1;
    double x[sample_num], y[sample_num];
    
    printf("途中で終了する場合は,Ctrl-C をターミナルに入力\n");
    
    for (i=0;i<step;i++){
        fprintf(gp,"set title 'animated-%d/%d' font 'Times, 20'\n",i,step);    
        t = i*dt;
        move(sample_num, t, x, y);
        plot2d(gp,sample_num, x, y); 
        usleep(30000); //指定された(マイクロ?)秒プログラムを停止させます (Unix system only)
//        while(getchar() != '\n');       /*Enter keyが入力されるまで,計算停止*/        
    }
    printf("計算終了\n");


    return 0;
}

void gnuplot_config(FILE *gp)
{
/*
    fprintf(gp,"set terminal gif animate optimize size \n");
    fprintf(gp,"set output 'anime.gif'\n");
*/
    /*アニメーションをgif動画として保存する場合は,上のコメントを解除*/    
    fprintf(gp,"set xrange [-1.5:1.5]\n");
    fprintf(gp,"set yrange [-1.5:1.5]\n");
    fprintf(gp,"unset tics\n");
//    fprintf(gp,"set xtics 0.5\n");
//    fprintf(gp,"set ytics 0.5\n");
//    fprintf(gp,"set xlabel 'x' font 'Times, 20'\n");
//    fprintf(gp,"set ylabel 'y' font 'Times, 20'\n");
//    fprintf(gp,"set grid\n");
    fprintf(gp,"set border 0\n");
    fprintf(gp,"set size square\n");
}

void plot2d(FILE *gp, const int num, const double *x, const double *y)
{
    int i;
    fprintf(gp,"plot");
    for(i=0;i<num;i++){
        if (i!=num-1)
            fprintf(gp,"'-' with p pt 7 ps 10 lc %d tit '',", i);//plotデータの読み込み開始
        else
            fprintf(gp,"'-' with p pt 7 ps 10 lc %d tit ''\n", i);//plotデータの読み込み開始
    }
    for(i=0;i<num;i++){
        fprintf(gp,"%f %f\n",x[i],y[i]);
        fprintf(gp,"e\n"); //データ書き込み終了を知らせる.
    }
    fflush(gp); //バッファーに溜まったデーターを吐き出す.これをしないと,real time animation にならない.
}


void move(const int num, const double t, double *x, double *y)
{
    int i;
    double init_phase[num];
    for (i =0;i<num;i++){
        init_phase[i] =  i*2.0*M_PI/num;
    }

    for (i=0;i<num;i++){
        x[i] = cos(t+init_phase[i]);
        y[i] = sin(t+init_phase[i]);
    }
}
計算結果はこんな感じに
HTMLって鍵括弧(<>)を&lt &gtって入力しないと行けないのか・・・プログラムコピペする時めんどくさいな・・・

今回はspleep関数使ってるけど,FPSとかってどうやって制御すれば良いんだ?

0 件のコメント:

コメントを投稿