/** 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); } } }