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