/** 1次元波動方程式と2次元波動方程式を求めるプログラミングです。
* マルチスレッドプログラミングを使っています。
* ダブルバッファリングを使っています。
*
* これをコンパイルするにはMitsui_and_Itoが必要です。
* MitsuiWorldが使える状態であれば普通のコンパイル javac WaveAll_2_1.java
* と普通の起動方法 appletviewer Wave1d_2_1.java
* でOK。
* ただし appletviewer WaveAll_2_1.java としたければコメント欄のどこかに
*
*
*
* というものが入ってないといけない。
* それとMitsuiWorldの使い方によっては
*
* import Mitsui.*;
*
* を消すこと。
* Mitsui_and_Itoをパッケージとして使わない人はこれを消してください
*
*/
import java.applet.*;
import java.awt.*;
import Mitsui.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.*;
public class WaveAll_2_1 extends Applet
implements Runnable,ActionListener,ItemListener{
Thread th = null; //スレッド
Button b_start,b_stop,b_next,b_back,b_reverse; //各ボタン
Button b_1dset,b_2dset;
Label l_time,l_area; //各ラベル
Label2 l_func;
Choice c_dimension,c_boundary; //関数を選ぶGUI
boolean runmove=false; //スレッドが動いてるか止まってるか
boolean reverseflag=false; //reverseの時に時間をマイナスにする
int im_t,re_t; //時間の経過
int wmax,hmax; //画面のサイズ
//もう一つの画面の定義
Graphics bg;
Image buf;
//画面作成に必要なもの
Mitsui_and_Ito m;
//2次元
double xmin2,xmax2,ymin2,ymax2,zmin2,zmax2;
int nx2,ny2;
double dx2,dy2;
double tau2;
//1次元
double xmin1,xmax1,ymin1,ymax1;
int nx1;
double dx1;
double tau1;
int n1=100;
int n2=100;
double[][] u2_1 = new double[n2+1][n2+1];
double[][] u2_2 = new double[n2+1][n2+1];
double[][] u2_3 = new double[n2+1][n2+1];
double[] u1_1 = new double[n1+1];
double[] u1_2 = new double[n1+1];
double[] u1_3 = new double[n1+1];
int nfunc1=0; //1次元の関数を変える
int nfunc2=0; //2次元の関数を変える
int dimension=0; //次元を変える/0=1次元,1=2次元
int boundary=0; //境界条件を変える
//視覚を変える変数
int lx,ly,lz; //各軸の長さ
int angx,angy; //視覚の角度
int x0,y0; //原点の位置
//初期設定
public void init(){
setLayout(new BorderLayout());
Panel p = new Panel(); //NORTH地区を枠決めしてる
p.setLayout(new GridLayout(3,1,0,0));
Panel p1 = new Panel(); //1段目
Panel p2 = new Panel(); //2段目
Panel p3 = new Panel(); //3段目
//startボタンの設置
b_start=new Button("START");
b_start.addActionListener(this);
p2.add(b_start);
//stopボタンの設置
b_stop=new Button("STOP");
b_stop.addActionListener(this);
p2.add(b_stop);
//reverseボタンの設置
b_reverse=new Button("REVERSE");
b_reverse.addActionListener(this);
p2.add(b_reverse);
//backボタンの設置
b_back=new Button("BACK");
b_back.addActionListener(this);
p2.add(b_back);
//nextボタンの設置
b_next=new Button("NEXT");
b_next.addActionListener(this);
p2.add(b_next);
//dimensionチョイスの設置
c_dimension=new Choice();
c_dimension.addItem("1次元波動方程式");
c_dimension.addItem("2次元波動方程式");
c_dimension.addItem("2次元(等高線)");
c_dimension.addItemListener(this);
p1.add(c_dimension);
//boundaryチョイスの設置
c_boundary=new Choice();
c_boundary.addItem("Dirichlet");
c_boundary.addItem("Neumann(素朴)");
c_boundary.addItem("Neumann(仮想格子点)");
c_boundary.addItemListener(this);
p1.add(c_boundary);
//1次元のセッティングを行うボタン
b_1dset = new Button("1次元の設定");
b_1dset.addActionListener(this);
p1.add(b_1dset);
//2次元のセッティングを行うボタン
b_2dset = new Button("2次元の設定");
b_2dset.addActionListener(this);
p1.add(b_2dset);
//ラベルl_timeの設置 時間を表示する。
l_time = new Label(" ");
p2.add(l_time);
//ラベルl_areaの設置 描写範囲の表示
l_area = new Label(" ");
p3.add(l_area);
//ラベルl_funcの設置 選んだ関数の表示
l_func = new Label2(
"φ=sin(πx) ",
"ψ=0 ");
p3.add(l_func);
p.add(p1);
p.add(p2);
p.add(p3);
add(p,"North");
//画面の横と縦の長さを得る。
wmax = getSize().width;
hmax = getSize().height;
//描写範囲を決める
//2次元[xmin2,xmax2]×[ymin2,ymax2]×[zmin2,zmax2]
xmin2 = 0.0;
xmax2 = 4.0;
ymin2 = 0.0;
ymax2 = 4.0;
zmin2 = -1.0;
zmax2 = 1.0;
//1次元[xmin1,xmax1]×[ymin1,ymax1]
xmin1 = -0.2;
xmax1 = 1.2;
ymin1 = -1.2;
ymax1 = 1.8;
//作図用の描写画面の作成
buf = createImage(wmax,hmax);
bg = buf.getGraphics();
//Mitsui_and_Ito
m = new Mitsui_and_Ito(); // mk
m.setGraphics(bg);
m.setScreenSize(wmax,hmax);
//分割数
nx1 = 100; //1次元のx分割数
nx2 = 40; //2次元のxの分割数大きすぎると動作が遅い
ny2 = 40; //2次元のyの分割数大きすぎると動作が遅い
//時刻間隔
tau1 = 0.01;
tau2 = 0.07;
//視覚を変える変数の初期値
lx=0;
ly=0;
lz=0;
x0=y0=0;
angx=angy=0;
}
//起動と同時にスレッドを走らせる
public void start(){
if(th==null){
th=new Thread(this);
th.start();
}
}
private void draw_contour() {
for (double h=-1.0;h<=1.0;h+=0.1) {
if (h < 0.0)
m.setColor(Color.blue);
else
m.setColor(Color.red);
l_time.setText("t="+re_t*tau2);
double[][] u = f2(im_t);
m.contln(xmin2, xmax2, ymin2, ymax2, u, nx2, ny2, h);
}
}
//スレッドの実装
public void run(){
while(true){ //起動中スレッドを走らせる。
while(!runmove){ //startボタンを押すことによって
} //ここからぬけ、スレッドが有効になる。
if(!reverseflag)
re_t++;
else
re_t--;
im_t++;
bg.clearRect(0,0,wmax,hmax); //画面をクリア
if(dimension==0){ //1次元波動方程式のとき
l_time.setText("t="+re_t*tau1);
double[] u = f1(im_t);
m.setColor(Color.black);
drawAxis();
m.setColor(Color.pink);
m.move(0.0,u[0]);
for(int i=1;i<=nx1;i++)
m.draw(i*dx1,u[i]);
}
if(dimension==1){ //2次元波動方程式のとき
l_time.setText("t="+re_t*tau2);
double[][] u = f2(im_t); //計算した結果を格納する
m.hideBirdView(u,nx2,ny2,1); //メモリ内で描く
}
if (dimension==2) {
// 等高線描画
draw_contour();
/*
for (double h=-1.0;h<=1.0;h+=0.1) {
if (h < 0.0)
m.setColor(Color.blue);
else
m.setColor(Color.red);
l_time.setText("t="+re_t*tau2);
double[][] u = f2(im_t);
m.contln(xmin2, xmax2, ymin2, ymax2, u, nx2, ny2, h);
}
}
*/
}
repaint();
try{
Thread.sleep(100); //100ミリ秒ストップ
}catch(InterruptedException e){}
}
}
//終了と同時にスレッドも終了
public void stop(){
if(th!=null){
th = null;
}
}
public void actionPerformed(ActionEvent e){
//startボタンを押したときのアクション
if(e.getSource()==b_start){
runmove=true; //再びグラフを描き始める
}
//stopボタンを押したときのアクション
if(e.getSource()==b_stop){
if(!runmove){ //一時停止のとき
reverseflag=false; //初期化
im_t=0;
re_t=0;
repaint();
}
else{ //これで一時停止
runmove=false;
}
}
//backボタンを押したときのアクション
if(e.getSource()==b_back){
if(!reverseflag)
re_t--;
else
re_t++;
if(!runmove){
if(dimension==0){
for(int i=0;i<=nx1;i++){
u1_2[i] = u1_1[i];
u1_1[i] = u1_3[i];
}
double[] u = f1(im_t);
for(int i=0;i<=nx1;i++){
u1_2[i] = u1_1[i];
u1_1[i] = u1_3[i];
u1_3[i] = u1_2[i];
}
l_time.setText("t="+(double)re_t*tau1);
bg.clearRect(0,0,wmax,hmax); //画面をクリア
m.setColor(Color.black);
drawAxis();
m.setColor(Color.pink);
m.move(0.0,u1_3[0]);
for(int i=1;i<=nx1;i++)
m.draw(i*dx1,u1_3[i]);
}
if(dimension==1 || dimension==2){
for(int i=0;i<=nx2;i++){
for(int j=0;j<=ny2;j++){
u2_2[i][j] = u2_1[i][j];
u2_1[i][j] = u2_3[i][j];
}
}
double[][] u = f2(im_t);
for(int i=0;i<=nx2;i++){
for(int j=0;j<=ny2;j++){
u2_2[i][j] = u2_1[i][j];
u2_1[i][j] = u2_3[i][j];
u2_3[i][j] = u2_2[i][j];
}
}
l_time.setText("t="+(double)re_t*tau2);
bg.clearRect(0,0,wmax,hmax); //画面をクリア
m.hideBirdView(u2_3,nx2,ny2,1); //メモリ内で描く
}
repaint();
}
}
//nextボタンを押したときのアクション
if(e.getSource()==b_next){
if(!runmove){ //一時停止状態のときのみ
if(!reverseflag)
re_t++;
else
re_t--; //次の時間のグラフを見れる。
bg.clearRect(0,0,wmax,hmax); //画面をクリア
if(dimension==0){
l_time.setText("t="+(double)re_t*tau1);
double[] u = f1(im_t);
m.setColor(Color.black);
drawAxis();
m.setColor(Color.pink);
m.move(0.0,u[0]);
for(int i=1;i<=nx1;i++)
m.draw(i*dx1,u[i]);
}
if(dimension==1){
l_time.setText("t="+(double)re_t*tau2);
double[][] u = f2(im_t); //計算した結果を格納する
m.hideBirdView(u,nx2,ny2,1); //メモリ内で描く
}
if (dimension == 2) {
draw_contour();
/*
for (double h=-1.0;h<=1.0;h+=0.1) {
if (h < 0.0)
m.setColor(Color.blue);
else
m.setColor(Color.red);
l_time.setText("t="+(double)re_t*tau2);
double[][] u = f2(im_t);
m.contln(xmin2, xmax2, ymin2, ymax2, u, nx2, ny2, h);
*/
}
repaint();
}
}
//reverseボタンを押したときのアクション
if(e.getSource()==b_reverse){
//逆流してるかしてないか。
if(reverseflag)
reverseflag=false;
if(!reverseflag)
reverseflag=true;
if(dimension==0){
double[]u = f1(im_t);
for(int i=0;i<=nx1;i++){
u1_2[i] = u1_1[i];
u1_1[i] = u1_3[i];
u1_3[i] = u1_2[i];
}
}
if(dimension==1 || dimension == 2){
double[][] u = f2(im_t);
for(int i=0;i<=nx2;i++){
for(int j=0;j<=ny2;j++){
u2_2[i][j]=u2_1[i][j];
u2_1[i][j]=u2_3[i][j];
u2_3[i][j]=u2_2[i][j];
}
}
}
}
//1次元波動方程式の設定をするダイアログを作っている。
if(e.getSource()==b_1dset){
String s = ("初期条件を選んでください");
//初期値選択欄の作成
Choice c_func1 = new Choice();
c_func1.addItem("固有振動");
c_func1.addItem("別れては重なる波");
c_func1.addItem("右方向に動く波");
c_func1.addItem("左方向に動く波");
c_func1.addItem("二つの波の合体");
//分割数記入欄の作成
NumericField inputN = new NumericField(""+nx1);
inputN.setBorder
(new TitledBorder("分割数を指定してください(=<100)"));
//時間刻み記入欄の作成
NumericField inputTau = new NumericField(""+tau1);
inputTau.setBorder
(new TitledBorder("時間刻みを指定してください"));
//描写範囲指定欄の作成
NumericField inputArea =
new NumericField(xmin1+" "+xmax1+" "+ymin1+" "+ymax1);
inputArea.setBorder
(new TitledBorder("描写範囲を指定してください 数の間には空白を入れて下さい。"));
//オブジェクトを一つにまとめて
Object[] obj = {s,c_func1,inputN,inputTau,inputArea};
//ダイアログの作成
int ans = JOptionPane.showConfirmDialog(this,obj,
"1次元波動方程式の設定",
JOptionPane.OK_CANCEL_OPTION);
if(ans==0){ //OKを押した時
nfunc1 = c_func1.getSelectedIndex();
if(dimension==0){
//式の記述
switch(nfunc1){
case 0:
l_func.setText("φ=sin(πx)","ψ=0");
break;
case 1:
l_func.setText("φ=(sin(π(10x-0.5))+1)/4",
"ψ=0");
break;
case 2:
l_func.setText("φ=(sin(π(10x-0.5))+1)/4",
"ψ=-2.5πcos(π(10x-0.5))");
break;
case 3:
l_func.setText("φ=(sin(π(10x-0.5))+1)/4",
"ψ=2.5πcos(π(10x-0.5))");
break;
case 4:
l_func.setText("φ1=(sin(π(10(x+0.4)-0.5))+1)/4",
"φ2=(sin(π(10(x-0.4)-0.5))+1)/4");
break;
default:
l_func.setText("未定","未定");
break;
}
}
nx1 = Integer.parseInt(inputN.getText().trim());
tau1 = Double.valueOf(inputTau.getText().trim()).doubleValue();
//描写範囲を読み込む時は「分割」をつかう。
String str = inputArea.getText().trim();
//空白区切りで分割する
StringTokenizer st = new StringTokenizer(str," ");
if(st.countTokens()==4){ //分割数が4のとき
xmin1 = Double.valueOf(st.nextToken()).doubleValue();
xmax1 = Double.valueOf(st.nextToken()).doubleValue();
ymin1 = Double.valueOf(st.nextToken()).doubleValue();
ymax1 = Double.valueOf(st.nextToken()).doubleValue();
}
runmove = false; //初期状態に戻す
reverseflag=false;
im_t=0;
re_t=0;
repaint();
}
}
if(e.getSource()==b_2dset){
String s = ("初期条件を選んでください");
Choice c_func2 = new Choice();
c_func2.addItem("φ=sin(πx)+sin(πy),ψ=0");
c_func2.addItem("定常波");
c_func2.addItem("x軸に平行な平面波");
c_func2.addItem("y軸に平行な平面波");
c_func2.addItem("平面波の合体");
c_func2.addItem("斜め方向の平面波");
//ダイアログは持ち込むオブジェクトを縦に並べる。しかし
//パネルを使うことにより横にもコンテンツを増やした。
Panel pdivide = new Panel();
JLabel N = new JLabel("分割数");
NumericField inputNx = new NumericField(""+nx2,10);
NumericField inputNy = new NumericField(""+ny2,10);
inputNx.setBorder(new TitledBorder("Nx(=<50)"));
inputNy.setBorder(new TitledBorder("Ny(=<50)"));
pdivide.add(N);
pdivide.add(inputNx);
pdivide.add(inputNy);
NumericField inputTau = new NumericField(""+tau2);
inputTau.setBorder
(new TitledBorder("時間刻みを指定してください"));
NumericField inputArea =
new NumericField(xmin2+" "+xmax2+" "+
ymin2+" "+ymax2+" "+zmin2+" "+zmax2);
inputArea.setBorder
(new TitledBorder("描写範囲の指定 数の間には空白を入れて下さい。"));
//角度を変える欄の作成
Panel pangle = new Panel();
JLabel A = new JLabel("角度変え(ラジアン)");
NumericField inputAngx = new NumericField(""+angx,10);
NumericField inputAngy = new NumericField(""+angy,10);
inputAngx.setBorder(new TitledBorder("水平方向"));
inputAngy.setBorder(new TitledBorder("垂直方向"));
pangle.add(A);
pangle.add(inputAngx);
pangle.add(inputAngy);
//原点移動の欄の作成
Panel porigin = new Panel();
JLabel O = new JLabel("原点移動(ピクセル)");
NumericField inputX0 = new NumericField(""+x0,10);
NumericField inputY0 = new NumericField(""+y0,10);
inputX0.setBorder(new TitledBorder("右向き"));
inputY0.setBorder(new TitledBorder("上向き"));
porigin.add(O);
porigin.add(inputX0);
porigin.add(inputY0);
//軸の長さ変更欄の作成
Panel plength = new Panel();
JLabel L = new JLabel("サイズ変え");
NumericField inputLx = new NumericField(""+lx,8);
NumericField inputLy = new NumericField(""+ly,8);
NumericField inputLz = new NumericField(""+lz,8);
inputLx.setBorder(new TitledBorder("x軸"));
inputLy.setBorder(new TitledBorder("y軸"));
inputLz.setBorder(new TitledBorder("z軸"));
plength.add(L);
plength.add(inputLx);
plength.add(inputLy);
plength.add(inputLz);
Object[] obj = {s,c_func2,pdivide,inputTau,inputArea,pangle,
porigin,plength};
int ans = JOptionPane.showConfirmDialog(this,obj,
"2次元波動方程式の設定",
JOptionPane.OK_CANCEL_OPTION);
if(ans==0){
nfunc2 = c_func2.getSelectedIndex();
if(dimension==1 || dimension == 2){
//式の記述
switch(nfunc2){
case 0:
l_func.setText("φ=sin(πx)+sin(πy)",
"ψ=0");
break;
case 1:
l_func.setText( "φ=(sin2πx)(sinπy)",
"ψ=0");
break;
case 2:
l_func.setText("φ=(sin(π(2x-0.5))+1)/4",
"ψ=-πcos(π(2x-0.5))/2");
break;
case 3:
l_func.setText("φ=(sin(π(2y-0.5))+1)/4",
"ψ=-πcos(π(2y-0.5))/2");
break;
case 4:
l_func.setText("φ1=(sin(π(2(x+1)-0.5))+1)/4",
"φ2=(sin(π(2(x-2)-0.5))+1)/4");
break;
case 5:
l_func.setText("φ=(sin(π(2(1/sqrt(2.0)(x+y))-0.5)+1)/4", "ψ=-πcos(π(2(1/sqrt(2.0)(x+y))-0.5))/2" );
break;
default:
l_func.setText("未定","未定");
break;
}
}
nx2 = Integer.parseInt(inputNx.getText().trim());
ny2 = Integer.parseInt(inputNy.getText().trim());
tau2 = Double.valueOf(inputTau.getText().trim()).doubleValue();
String str = inputArea.getText().trim();
StringTokenizer st = new StringTokenizer(str," ");
if(st.countTokens()==6){
xmin2 = Double.valueOf(st.nextToken()).doubleValue();
xmax2 = Double.valueOf(st.nextToken()).doubleValue();
ymin2 = Double.valueOf(st.nextToken()).doubleValue();
ymax2 = Double.valueOf(st.nextToken()).doubleValue();
zmin2 = Double.valueOf(st.nextToken()).doubleValue();
zmax2 = Double.valueOf(st.nextToken()).doubleValue();
}
angx = Integer.parseInt(inputAngx.getText().trim());
angy = Integer.parseInt(inputAngy.getText().trim());
x0 = Integer.parseInt(inputX0.getText().trim());
y0 = Integer.parseInt(inputY0.getText().trim());
lx = Integer.parseInt(inputLx.getText().trim());
ly = Integer.parseInt(inputLy.getText().trim());
lz = Integer.parseInt(inputLz.getText().trim());
runmove = false; //初期状態に戻す
reverseflag=false;
im_t=0;
re_t=0;
repaint();
}
}
}
//dimension,boundary,fundチョイスを選んだとき
public void itemStateChanged(ItemEvent e){
dimension=c_dimension.getSelectedIndex(); //次元番号を変える
boundary =c_boundary.getSelectedIndex(); //境界条件番号を変える
runmove = false; //初期状態に戻す
reverseflag=false;
im_t=0;
re_t=0;
repaint();
}
public void paint(Graphics g){
if(im_t==0){
bg.clearRect(0,0,wmax,hmax);
if(dimension==0){
dx1 = 1.0/nx1; //1次元のx格子点
l_time.setText("t=0.0"); //時刻と描写範囲の表示
l_area.setText("["+xmin1+","+xmax1+"]×["+ymin1+","+ymax1+"]");
m.setArea(xmin1,xmax1,ymin1,ymax1);
double[] u = f1(im_t);
m.setColor(Color.black);
drawAxis();
m.setColor(Color.pink);
m.move(0.0,u[0]);
for(int i=1;i<=nx1;i++)
m.draw(i*dx1,u[i]);
}
if(dimension==1 || dimension==2){
dx2 = (xmax2-xmin2)/nx2; //2次元のx格子点
dy2 = (ymax2-ymin2)/ny2; //2次元のy格子点
l_time.setText("t=0.0"); //時刻と描写範囲の表示
l_area.setText("["+xmin2+","+xmax2+"]×["+ymin2+","+ymax2+
"]×["+zmin2+","+zmax2+"]");
//視覚を変えている
m.changeAngle(angx,angy);
m.changePosition(x0,y0);
m.changeSize(lx,ly,lz);
//描き始める。
m.setArea2(xmin2,xmax2,ymin2,ymax2,zmin2,zmax2);
double[][] u = f2(im_t);
m.setColor(Color.pink);
m.hideBirdView(u,nx2,ny2,1);
}
}
g.drawImage(buf,0,0,this); //もう一つの画面をくっつける
}
public double[][] f2(int t){
double lambdax = tau2 / dx2;
double lambday = tau2 / dy2;
double lambdax2 = lambdax * lambdax;
double lambday2 = lambday * lambday;
if(t==0){ //t=0のとき
for(int i=0;i<=nx2;i++)
for(int j=0;j<=ny2;j++)
u2_1[i][j] = phi(xmin2+i*dx2,ymin2+j*dy2); //初期値代入
return u2_1;
}
if(t==1){
for(int i=1;i=2のとき
for(int i=1;i=-1;i-=0.2){
m.move(-0.01,i); m.draw(0.01,i);
}
}
//数字しか入らないテキストフィールドの作成
class NumericField extends JTextField{
public NumericField(String begin){
super(begin);
enableEvents(AWTEvent.KEY_EVENT_MASK);
}
public NumericField(String begin,int num){
super(begin,num);
enableEvents(AWTEvent.KEY_EVENT_MASK);
}
protected void processKeyEvent(KeyEvent e){
String validValues = "0123456789.- ";
int code = e.getKeyCode();
switch(code){
case 39:break; //右カーソル
case 37:break; //左カーソル
default:
char chr = e.getKeyChar();
if(chr >= ' ' && validValues.indexOf( chr )==-1){
return;
}
break;
}
super.processKeyEvent(e);
}
}
//2行にまたがるラベル。
class Label2 extends JComponent{
JLabel row1,row2;
public Label2(String s1,String s2){
setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
row1 = new JLabel(s1);
row2 = new JLabel(s2);
add(row1);
add(row2);
}
public void setText(String s1,String s2){
row1.setText(s1);
row2.setText(s2);
}
}
}