/** 1次元波動方程式と2次元波動方程式を求めるプログラミングです。
* マルチスレッドプログラミングを使っています。
* ダブルバッファリングを使っています。
*
* これをコンパイルするにはMitsuiWorldが必要です。
* MitsuiWorldが使える状態であれば普通のコンパイル javac WaveAll_2_0.java
* と普通の起動方法 appletviewer Wave1d.java
* でOK。
* ただし appletviewer WaveAll_2_0.java としたければコメント欄のどこかに
*
*
*
* というものが入ってないといけない。
* それとMitsuiWorldの使い方によっては
*
* import Mitsui.*;
*
* を消すこと。
* MitsuiWorldをパッケージとして使わない人はこれを消してください
*
*/
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_0 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;
//画面作成に必要なもの
MitsuiWorld_2 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=50;
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.addItemListener(this);
p1.add(c_dimension);
//boundaryチョイスの設置
c_boundary=new Choice();
c_boundary.addItem("Dirichlet");
c_boundary.addItem("Neumann");
c_boundary.addItem("仮想格子点");
c_boundary.addItemListener(this);
p1.add(c_boundary);
//1次元のセッティングを行うボタン
b_1dset = new Button("1Dsetting");
b_1dset.addActionListener(this);
p1.add(b_1dset);
//2次元のセッティングを行うボタン
b_2dset = new Button("2Dsetting");
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 = -2.0;
xmax2 = 2.5;
ymin2 = -2.0;
ymax2 = 2.5;
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();
//MitsuiWorld
m = new MitsuiWorld_2();
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();
}
}
//スレッドの実装
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); //メモリ内で描く
}
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){
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); //メモリ内で描く
}
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){
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("二つの波の合体");
//分割数記入欄の作成
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("φ=8x-3(3/8=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);
}
}
}