next up previous contents
Next: D. 他の人が作ったパッケージの利用 Up: C. 学生のプログラム Previous: C..2 波動方程式

C..3 渦糸

田代航平君の渦糸のプログラム (2003年2月11日) は やはり http://nalab.mind.meiji.ac.jp/~mk/labo/java/ から入手できる。 実行は http://nalab.mind.meiji.ac.jp/~mk/labo/java/uzu.htmlで出来る。


// 渦糸系の力学系のシミュレーション
// (c)kou
// 2003.02.11完成
// <applet code="vortex.class" width=600 height=600></applet>
// modified by mk (2003/3/19) Runge-Kutta 法を使うように変更

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

final public class vortex extends Applet implements Runnable, ActionListener {

  private Image off;        // オフスクリーン
  private Graphics grf;     // オフスクリーンのグラフィックス

  private Thread th = null; // スレッド
        
  private Dimension d;      // アプレットのサイズ
  private Dimension imgD;   // オフスクリーンのサイズ
        
  private double dt = 0.2;  // 時刻の増分 (刻み幅)
  private int N = 5;        // 渦糸の個数
  private int MaxN = 12;
  private double scale = 20;// 拡大率
  private int sleepTime=10; // スレッドの待ち時間
  private double[] Gamma;   // 渦の強さ (ガンマ)
  private double[] Z;       // 渦糸の位置 (k番目の渦の位置は (Z[2*k],Z[2*k+1]))
  private double [] Z2, f, k1, k2, k3, k4; // Runge-Kutta 法の計算用変数
        
  private String[] btString = {"Start", "End"}; // ボタンの名前
  private Button[] bt; // ボタン
        
  private String[] laString1={"dt","N","scale","sleepTime"}; // ラベルの名前
  private String[] laString2={"k","Γ","X","Y"};             // ラベルの名前

  private Label[] la1, la2, la3; // ラベル
  private TextField[] tf1;       // ラベルに対するテキストフィールド

  private TextField[] tfGamma;   // Γの値のテキストフィールド
  private TextField[] tfX;       // 渦糸の初期位置のx座標のテキストフィールド
  private TextField[] tfY;       // 渦糸の初期位置のy座標のテキストフィールド
  
  private Color[] color1;        // 色合いの薄い色 (未使用!)
  private Color[] color2;        // 色合いが中くらいの色

  public void init() {
    int i; // カウンタ
                
    setBackground(Color.white);//背景色の設定
                
    d=getSize();//アプレットのサイズの取得
    imgD=new Dimension(d.width-200,d.height);//オフスクリーンの色の設定
                
    // 色の作成
    color1 = new Color[MaxN];
    color2 = new Color[MaxN];
    for (i = 0; i < MaxN;i++) {
      float a = i / (float)MaxN;
      color1[i]=Color.getHSBColor(a, 0.2f, 1.0f);
      color2[i]=Color.getHSBColor(a, 0.5f, 1.0f);
    }
    // オフスクリーンの準備
    off = createImage(imgD.width,imgD.height); // オフスクリーンの作成
    grf = off.getGraphics();                   // グラフィックスの取得
    // オフスクリーンのクリア
    grf.setColor(Color.black);                 // 背景色の設定
    grf.fillRect(0,0,imgD.width,imgD.height);  // 塗りつぶす
    // オフスクリーンに座標軸を描く
    grf.setColor(Color.gray);                  // 軸の色の設定
    grf.drawLine(0,imgD.height/2,imgD.width,imgD.height/2); // x軸
    grf.drawLine(imgD.width/2,0,imgD.width/2,imgD.height);  // y軸

    // レイアウトマネージャを使わない (自分で設定する))
    setLayout(null);

    // Start, End ボタンの準備
    bt= new Button[btString.length]; // ボタンの配列の生成
    for (i = 0; i < bt.length; i++) {
      bt[i] = new Button(btString[i]); // ボタンの生成
      bt[i].addActionListener(this);   // リスナーに関連付け
      bt[i].setBounds(imgD.width,20*i,d.width-imgD.width,20);//ボタンの位置決め
      add(bt[i]);                      //ボタンの登録
    }

    // dt, N, scale, sleepTime のラベル、テキスト・フィールドの準備
    la1 = new Label[laString1.length];     // ラベルの宣言
    tf1 = new TextField[laString1.length]; // テキストフィールドの宣言
    for (i = 0; i < la1.length; i++) {
      la1[i] = new Label(laString1[i]);    // ラベルの生成
      la1[i].setBounds(imgD.width,
                       20*(i+bt.length),
                       (d.width-imgD.width)/2,
                       20);                // ラベルの位置決め
      add(la1[i]);                         // ラベルの登録
                        
      tf1[i]=new TextField();              // テキストフィールドの生成
      tf1[i].setBounds(imgD.width+(d.width-imgD.width)/2,
                       20*(i+bt.length),
                       (d.width-imgD.width)/2,
                       20);                // テキストフィールドの位置決め
      add(tf1[i]);                         // テキストフィールドの登録
    }

    // k, Γ, X, Y というラベルの準備
    la2 = new Label[laString2.length];     // ラベルの配列の生成
    for (i = 0; i < laString2.length; i++) {
      la2[i] = new Label(laString2[i]);    // ラベルの生成
      la2[i].setBounds(imgD.width+(d.width-imgD.width)*i/4,
nnn                       20*(bt.length+la1.length),
                       (d.width-imgD.width)/4,
                       20);                // ラベルの位置決め
      add(la2[i]);                         // ラベルの登録
    }

    // 各渦糸のデータ (強さ、初期位置) のラベル、テキストフィールド
    la3 = new Label[MaxN]; // ラベルの配列の生成
    tfGamma = new TextField[MaxN];         // テキストフィールドの生成
    tfX = new TextField[MaxN];             // テキストフィールドの生成
    tfY = new TextField[MaxN];             // テキストフィールドの生成
    for (i = 0; i < la3.length; i++) {
      la3[i] = new Label("" + (i + 1));    // ラベルの生成
      la3[i].setBounds(imgD.width+(d.width-imgD.width)*0/4,
                       20*(bt.length+la1.length+1+i),
                       (d.width-imgD.width)/4,
                       20);                // ラベルの位置決め
      la3[i].setBackground(color2[i]);     // 背景の設定
      add(la3[i]);                         // ラベルの登録

      tfGamma[i] = new TextField(); // テキストフィールドの生成
      tfGamma[i].setBounds(imgD.width+(d.width-imgD.width)*1/4,
                           20*(bt.length+la1.length+1+i),
                           (d.width-imgD.width)/4,
                           20); // ラベルの位置決め
      add(tfGamma[i]); // テキストフィールドの登録

      tfX[i]=new TextField(); // テキストフィールドの生成
      tfX[i].setBounds(imgD.width+(d.width-imgD.width)*2/4,
                       20*(bt.length+la1.length+1+i),
                       (d.width-imgD.width)/4,
                       20); // ラベルの位置決め
      add(tfX[i]); // テキストフィールドの登録

      tfY[i]=new TextField(); // テキストフィールドの生成
      tfY[i].setBounds(imgD.width+(d.width-imgD.width)*3/4,
                       20*(bt.length+la1.length+1+i),
                       (d.width-imgD.width)/4,
                       20); // ラベルの位置決め
      add(tfY[i]); // テキストフィールドの登録
    }

    // 計算用の配列を確保する
    Gamma = new double[MaxN];
    Z  = new double[2*MaxN];
    Z2 = new double[2*MaxN];
    f  = new double[2*MaxN];
    k1 = new double[2*MaxN];
    k2 = new double[2*MaxN];
    k3 = new double[2*MaxN];
    k4 = new double[2*MaxN];

    // 渦の強さ, 初期位置の決定
    for (i = 0; i < MaxN; i++) {
      Gamma[i]=1;
      Z[2*i]   = Math.pow(-1.0,i)*i;
      Z[2*i+1] = 0;
    }
    // 渦の強さ, 初期位置をテキストフィールドに表示
    tf1[0].setText("" + dt);
    tf1[1].setText("" + N);
    tf1[2].setText("" + scale);
    tf1[3].setText("" + sleepTime);
    for (i = 0; i < tfGamma.length; i++) {
      tfGamma[i].setText("" + Gamma[i]);
      tfX[i].setText("" + Z[2*i]);
      tfY[i].setText("" + Z[2*i+1]);
    }

    // スタートボタンを無効化
    bt[0].setEnabled(false);
  }
  
  public void paint(Graphics g) {
    update(g);
  }
  public void update(Graphics g) {
    g.drawImage(off, 0, 0, this); // オフスクリーンの表示
  }
  // スレッドを生成してスタート
  public void start() {
    if (th == null) {
      th = new Thread(this);
      th.start();
    }
  }
  // スレッドを止める
  public void stop() {
    if (th != null) {
      th=null;
    }
  }
  // 座標変換 (x座標)
  private int scx(double x) {
    return (int)(scale * x) + imgD.width / 2;
  }
  // 座標変換 (y座標)
  private int scy(double y) {
    return - (int)(scale * y) + imgD.height / 2;
  }
  // ユーザーの座標系で線分を描く
  private void MydrawLine(double x1, double y1, double x2, double y2) {
    grf.drawLine(scx(x1), scy(y1), scx(x2), scy(y2));
  }
  // 渦糸系のベクトル場 f(z) の計算
  public void aux(double []z, double []f, int N) {
    double doublePI = 2.0 * Math.PI;
    double sumx, sumy;
    for (int i = 0; i< N; i++) {
      sumx= 0; sumy= 0;
      for (int j = 0; j < N; j++)
        if (j != i) {
          double seki = Gamma[j] / (sqr(z[2*i]-z[2*j])+sqr(z[2*i+1]-z[2*j+1]));
          sumx -= seki * (z[2*i+1]-z[2*j+1]);
          sumy += seki * (z[2*i]-z[2*j]);
        }
        f[2*i+1] = sumy / doublePI;
        f[2*i]   = sumx / doublePI;
    }
  }
  // 計算スレッド
  public void run() {
    int i;
    int n = 2 * N; // 力学系の次元
    // オリジナルではここで配列の宣言をしていたが、それでは遅すぎる
    // double [] Z2 = new double[n];
    // double [] f  = new double[n];
    // double [] k1 = new double[n];
    // double [] k2 = new double[n];
    // double [] k3 = new double[n];
    // double [] k4 = new double[n];

    while (th != null) {
      // Runge-Kutta: next Z = Z + (k1 + 2 k2 + 2 k3 + k4) / 6
      // k1
      aux(Z, f, N);
      for (i = 0; i < n; i++)
        k1[i] = dt * f[i];
      // k2
      for (i = 0; i < n; i++)
        Z2[i] = Z[i] + 0.5 * k1[i];
      aux(Z2, f, N);
      for (i = 0; i < n; i++)
        k2[i] = dt * f[i];
      // k3
      for (i = 0; i < n; i++)
        Z2[i] = Z[i] + 0.5 * k2[i];
      aux(Z2, f, N);
      for (i = 0; i < n; i++)
        k3[i] = dt * f[i];
      // k4
      for (i = 0; i < n; i++)
        Z2[i] = Z[i] + k3[i];
      aux(Z2, f, N);
      for (i = 0; i < n; i++)
        k4[i] = dt * f[i];
      // 次のステップの値 (Runge-Kutta)
      for (i = 0; i < n; i++)
        Z2[i] = Z[i] + (k1[i] + 2 * (k2[i] + k3[i]) + k4[i]) / 6;
      // 軌跡を描く
      for (int k = 0; k < N; k++) {
        grf.setColor(color2[k]);
        MydrawLine(Z[2*k], Z[2*k+1], Z2[2*k], Z2[2*k+1]);
      }
      // 値の更新
      for (i = 0; i < n; i++)
        Z[i] = Z2[i];
      // オフスクリーンを描画する
      repaint(); 
      // スレッドの処理 (田代君の真似)
      if (sleepTime > 0) {
        try {
          th.sleep(sleepTime); //スレッドを一時停止
        }
        catch(InterruptedException e) {};
      }
      th.yield(); //スレッドを一時譲る
    }
  }
  // イベントの処理
  public void actionPerformed(ActionEvent e) {
    if (e.getSource() == bt[0]) {
      // Startボタンが押されたら
      //スタートボタンを無効化, エンドボタンを有効化してフォーカスを移す
      bt[0].setEnabled(false);
      bt[1].setEnabled(true);
      bt[1].requestFocus();

      // テキストフィールドの値を変数に代入
      dt = Double.valueOf(tf1[0].getText()).doubleValue();
      N = Integer.parseInt(tf1[1].getText());
      scale = Double.valueOf(tf1[2].getText()).doubleValue();
      sleepTime = Integer.parseInt(tf1[3].getText());
      for (int i = 0; i < MaxN; i++) {
        Gamma[i] = Double.valueOf(tfGamma[i].getText()).doubleValue();
        Z[2*i]   = Double.valueOf(tfX[i].getText()).doubleValue();
        Z[2*i+1] = Double.valueOf(tfY[i].getText()).doubleValue();
      }
      // N の範囲をチェックする
      if (N > MaxN) {
        // N が最大値を超えていたら MaxN にする
        N = MaxN;
        tf1[1].setText("" + N); //数値をテキストフィールドに表示
      }
      if (N < 1) {
        // N が最大値を超えていたら MaxN にする
        N = 1;
        tf1[1].setText("" + N); //数値をテキストフィールドに表示
      }
      // オフスクリーンのクリア
      grf.setColor(Color.black);                // 背景色の設定
      grf.fillRect(0,0,imgD.width,imgD.height); // 塗りつぶす (クリア)
      grf.setColor(Color.gray);                 // 軸の色の設定
      grf.drawLine(0,imgD.height/2,imgD.width,imgD.height/2); // x軸
      grf.drawLine(imgD.width/2,0,imgD.width/2,imgD.height);  // y軸
      // スレッドの開始
      start();
    }
    else if (e.getSource() == bt[1]) {
      // Endボタンが押されたら
      bt[0].setEnabled(true);  // スタートボタンを有効化
      bt[0].requestFocus();    // スタートボタンにフォーカスを移す
      bt[1].setEnabled(false); // エンドボタンを無効化
      // スレッドをとめる
      stop();
    }
  }
  // 二乗を返す
  private double sqr(double a) {
    return a * a;
  }
}


next up previous contents
Next: D. 他の人が作ったパッケージの利用 Up: C. 学生のプログラム Previous: C..2 波動方程式
Masashi Katsurada
平成20年2月28日