/* * NewHeat1Dv4.java --- 1次元熱方程式 * コンパイルはもちろん javac NewHeat1Dv4.java * 実行は java NewHeat1Dv4 * スレッド周辺はまだ不完全というか良く分かっていない。 * TMPEGEnc で MPEG ファイルを作るためにイメージのサイズを 8 の倍数にした。 * 実際に MPEG ファイルを作ることに成功した。 * 映像ソースとして最初の JPEG ファイルを指定し、 * ストリームの種類 System(Videoのみ), 設定でフレーム・レートを 7.992fps (つまり標準÷3) * そして圧縮開始するだけである。 * * 今後の改良計画 * (1) 境界条件色々に対応 * http://www.math.meiji.ac.jp/~mk/labo/text/head-fdm-1.pdf 参照 * (2) スレッドをきちんと理解してきちんと書く * (3) 2次元化する */ package jp.tuyano.eclipsebook3; import java.awt.Button; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Label; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; public class NewHeat1Dv4 extends Frame implements Runnable,ActionListener { private static final long serialVersionUID = 1L; /** * */ static final int ImgX = 496, ImgY = 496; static final int WinX = ImgX + 200, WinY = ImgY; Thread th = null; BufferedImage im = null; Graphics bg = null; int i, n = -1, nMax; int N; double lambda, Tmax, dt, theta; double h, tau; int skip; double [] x, u, newu; double ratiox, ratioy, X0, Y0; private boolean result = false; private int jpeg_file_number = 0; // GUI private String[] labelStr={"N", "lamba", "Tmax", "dt", "theta"}; private Label[] label; private String[] txStr={"100", "0.5", "1.0", "0.01", "0.5"}; private TextField[] tf; private String[] btStr = {"Start", "End"}; private Button[] bt; // テキスト・フィールドに入力されたパラメーターを読む private void readParameters() { N = Integer.parseInt(tf[0].getText()); lambda = Double.valueOf(tf[1].getText()).doubleValue(); Tmax = Double.valueOf(tf[2].getText()).doubleValue(); dt = Double.valueOf(tf[3].getText()).doubleValue(); theta = Double.valueOf(tf[4].getText()).doubleValue(); } public NewHeat1Dv4() { this.setSize(WinX, WinY); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent ev) { System.exit(0); } }); setLayout(null); bt = new Button[btStr.length]; for (int i = 0; i < bt.length; i++) { bt[i] = new Button(btStr[i]); bt[i].addActionListener(this); bt[i].setBounds(ImgX, (i+2)*20, (WinX - ImgX)/2, 20); add(bt[i]); } label = new Label[labelStr.length]; tf = new TextField[txStr.length]; for (int i = 0; i < labelStr.length; i++) { label[i] = new Label(labelStr[i]); tf[i] = new TextField(txStr[i]); label[i].setBounds(ImgX, (i+bt.length+2)*20, (WinX - ImgX) / 3, 20); tf[i].setBounds(ImgX + (WinX-ImgX)/3, (i+bt.length+2)*20, (WinX-ImgX)/2, 20); add(label[i]); add(tf[i]); } this.setVisible(true); bt[0].setEnabled(false); // ダブル・バッファリング用のバッファーを準備 if (im == null) { im = new BufferedImage(ImgX, ImgY, BufferedImage.TYPE_3BYTE_BGR); bg = im.getGraphics(); } space(-0.1, -0.1, 1.1, 1.1); // 計算開始の準備 initcomputation(); // 計算スレッドを開始する this.start(); } public void start() { if (th == null) { th = new Thread(this); th.start(); } } public void stop() { if (th != null) { th = null; } } public static void main(String[] args) { new NewHeat1Dv4(); } // 座標変換の仕組み private void space(double x0, double y0, double x1, double y1) { X0 = x0; Y0 = y0; ratiox = ImgX / (x1 - x0); ratioy = ImgY / (y1 - y0); } // x座標をユーザー座標からウィンドウ座標に private int wx(double x) { return (int)Math.rint(ratiox * (x - X0)); } // y座標をユーザー座標からウィンドウ座標に private int wy(double y) { return ImgY - (int)Math.rint(ratioy * (y - Y0)); } // 初期データ private double f(double x) { if (x <= 0.5) return x; else return 1.0 - x; } // 現在の u[] をグラフ化する private void drawGraph() { for (int i = 0; i < N; i++) bg.drawLine(wx(x[i]), wy(u[i]), wx(x[i+1]), wy(u[i+1])); } // ダブルバッファリングの定跡 public void paint(Graphics g) { g.drawImage(im, 0, 0, this); } // 計算の準備 (パラメーターを読み、変数の準備) private void initcomputation() { // パラメーターを読む readParameters(); // 刻み幅を決める h = 1.0 / N; tau = lambda * h * h; // skip = (int)Math.rint(dt / tau); if (skip <= 0) skip = 1; nMax = (int)Math.ceil(Tmax / tau); // ベクトルを3本用意する x = new double [N+1]; u = new double [N+1]; newu = new double [N+1]; // n = -1; } // 現在のバッファー im の内容を JPEG 形式で書き出す void saveJpeg() { String str; if (jpeg_file_number < 10) str = "0000" + jpeg_file_number; else if (jpeg_file_number < 100) str = "000" + jpeg_file_number; else if (jpeg_file_number < 1000) str = "00" + jpeg_file_number; else if (jpeg_file_number < 10000) str = "0" + jpeg_file_number; else str = "" + jpeg_file_number; try { result = ImageIO.write(im, "jpeg", new File("test" + str + ".jpg")); jpeg_file_number++; } catch (Exception e) { e.printStackTrace(); result = false; } } // 計算スレッド public void run() { while (th != null) { if (n == -1) { // 初期値を計算する for (int i = 0; i <= N; i++) x[i] = i * h; for (i = 0; i <= N; i++) u[i] = f(i * h); n++; repaint(); bg.setColor(Color.white); bg.fillRect(0, 0, ImgX, ImgY); bg.setColor(Color.black); drawGraph(); saveJpeg(); repaint(); try { Thread.sleep(10); } catch (Exception ex) { ex.printStackTrace(); } } else if (n < nMax) { do { for (i = 1; i < N; i++) newu[i] = (1 - 2 * lambda) * u[i] + lambda * (u[i + 1] + u[i - 1]); for (i = 1; i < N; i++) u[i] = newu[i]; u[0] = 0.0; u[N] = 0.0; n++; } while (n % skip != 0); drawGraph(); saveJpeg(); repaint(); try { Thread.sleep(10); } catch (Exception ex) { ex.printStackTrace(); } // System.out.println(""+ n * tau); } } } public void actionPerformed(ActionEvent e) { // TODO 自動生成されたメソッド・スタブ if (e.getSource() == bt[0]) { bt[0].setEnabled(false); bt[1].requestFocus(); bt[1].setEnabled(true); initcomputation(); start(); } if (e.getSource() == bt[1]) { bt[0].setEnabled(true); bt[0].requestFocus(); bt[1].setEnabled(false); stop(); } } }