/********************************/ /* 伝達関数のゲインと位相の計算 */ /* coded by Y.Suganuma */ /********************************/ import java.awt.*; import java.awt.event.*; import java.io.*; import java.text.*; public class Test { public static void main (String[] args) { BodePlot bd = new BodePlot("ボード線図"); } } class BodePlot extends Frame implements ActionListener, TextListener { double fl, fu, si[][], bo[][]; int k, m[], n[], n_g = -1; TextField fl_t, fu_t, k_t, n_g_t, ex[]; TextArea ta; Button bt; Coef bunsi[], bunbo[]; ScrollPane sp; /******************/ /* コンストラクタ */ /******************/ BodePlot(String name) { // Frameクラスのコンストラクタ(Windowのタイトルを引き渡す) super(name); // Windowの大きさ setSize(790, 590); // レイアウト,背景色,フォント setLayout(new BorderLayout(5, 10)); setBackground(new Color(225, 255, 225)); Font f = new Font("TimesRoman", Font.BOLD, 20); setFont(f); // 上のパネル Panel pn1 = new Panel(); pn1.setLayout(new GridLayout(2, 1, 10, 10)); add(pn1, BorderLayout.NORTH); Panel pn11 = new Panel(); pn1.add(pn11); pn11.add(new Label("式の数(グラフの数)")); n_g_t = new TextField(3); n_g_t.addTextListener(this); pn11.add(n_g_t); pn11.add(new Label(" ")); bt = new Button("実行"); bt.setBackground(Color.pink); bt.addActionListener(this); pn11.add(bt); Panel pn12 = new Panel(); pn1.add(pn12); pn12.add(new Label("周波数下限")); fl = 0.01; fl_t = new TextField("0.01", 3); fl_t.addTextListener(this); pn12.add(fl_t); pn12.add(new Label(" 周波数上限")); fu = 100.0; fu_t = new TextField("100", 3); fu_t.addTextListener(this); pn12.add(fu_t); pn12.add(new Label(" データ数")); k = 100; k_t = new TextField("100", 3); k_t.addTextListener(this); pn12.add(k_t); // 中央のパネル sp = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); add(sp, BorderLayout.CENTER); // 下のパネル Panel pn6 = new Panel(); add(pn6, BorderLayout.SOUTH); ta = new TextArea(5, 50); pn6.add(ta); // ウィンドウを表示 setVisible(true); // イベントアダプタ addWindowListener(new WinEnd()); } /************/ /* log10(x) */ /************/ static double log10(double x) { return Math.log(x) / Math.log(10.0); } /****************************************/ /* 伝達関数のsにjωを代入した値の計算 */ /* ff : ω(周波数) */ /* m : 分子の次数 */ /* si : 分子多項式の係数 */ /* n : 分母の次数 */ /* bo : 分母多項式の係数 */ /* return : 結果 */ /****************************************/ static Complex G_s(double ff, int m, double si[], int n, double bo[]) { Complex f, x, y; // 周波数を複素数に変換 f = new Complex (0.0, ff); // 分子 x = value(f, m, si); // 分母 y = value(f, n, bo); return Complex.dev(x, y); } /**************************************/ /* 多項式のsにjωを代入した値の計算 */ /* f : jω(周波数,複素数) */ /* n : 多項式の次数 */ /* a : 多項式の係数 */ /* return : 結果 */ /**************************************/ static Complex value(Complex f, int n, double a[]) { int i1, k1; Complex x; x = new Complex (0.0, 0.0); for (i1 = 0; i1 <= n; i1++) x = Complex.add(x, Complex.mul(new Complex(a[i1]), Complex.pow(f, i1))); return x; } /******************************/ /* 上,左,下,右の余白の設定 */ /******************************/ public Insets getInsets() { return new Insets(70, 20, 20, 20); } /******************************/ /* ボタンが押されたときの処理 */ /******************************/ public void actionPerformed(ActionEvent e) { double x, h, f, ff, g_min, g_max, p_min, p_max, uc = 90.0 / Math.asin(1.0); int i1, i2, k1, p, sw = 0; String g_title[] = null; Complex g; ta.setForeground(Color.black); ta.setText(""); // エラーの判定 if (n_g < 0) { sw = 1; ta.setForeground(Color.red); ta.append(" 式の数を入力してください\n"); } else { g_title = new String [n_g]; for (i1 = 0; i1 < n_g; i1++) { if (ex[i1].getText().length() <= 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の説明(凡例)を入力してください\n"); } else g_title[i1] = ex[i1].getText(); } for (i1 = 0; i1 < n_g; i1++) { if (m[i1] < 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分子の次数を入力してください\n"); } else { for (i2 = 0; i2 <= m[i1]; i2++) { if (bunsi[i1].bun_t[i2].getText().length() <= 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分子の s の " + i2 + " 次の係数を入力してください\n"); } else si[i1][i2] = Double.parseDouble(bunsi[i1].bun_t[i2].getText()); } } } for (i1 = 0; i1 < n_g; i1++) { if (n[i1] < 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分母の次数を入力してください\n"); } else { for (i2 = 0; i2 <= n[i1]; i2++) { if (bunbo[i1].bun_t[i2].getText().length() <= 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分母の s の " + i2 + " 次の係数を入力してください\n"); } else bo[i1][i2] = Double.parseDouble(bunbo[i1].bun_t[i2].getText()); } } } } // ゲインと位相の計算 if (sw == 0) { // 下限と上限の再設定 if (fl < 1.0) { x = 0.1; while (fl < x-1.0e-10) x /= 10.0; fl = x; } else { x = 1.0; while (fl > x-1.0e-10) x *= 10.0; fl = x; } if (fu < 1.0) { x = 0.1; while (fu < x+1.0e-10) x /= 10.0; fu = 10.0 * x; } else { x = 1.0; while (fu > x+1.0e-10) x *= 10.0; fu = x; } // 初期設定 double freq1[][] = new double [n_g][k+1]; double freq2[][] = new double [n_g][k+1]; double gain[][] = new double [n_g][k+1]; double phase[][] = new double [n_g][k+1]; h = (log10(fu) - log10(fl)) / k; g_min = 0.0; g_max = 0.0; p_min = 0.0; p_max = 0.0; ta.setText(" 角周波数 ゲイン(dB) 位相(度)\n"); for (i1 = 0; i1 < n_g; i1++) { ff = log10(fl); ta.append(g_title[i1] + "\n"); for (i2 = 0; i2 <= k; i2++) { // 周波数の対数を元に戻す f = Math.pow(10.0, ff); freq1[i1][i2] = f; freq2[i1][i2] = f; ta.append(" " + f); // 値の計算 g = G_s(f, m[i1], si[i1], n[i1], bo[i1]); // ゲインと位相の計算 gain[i1][i2] = 20.0 * log10(Complex.abs(g)); ta.append(" " + gain[i1][i2]); if (i1 == 0 && i2 == 0) { g_min = gain[i1][i2]; g_max = gain[i1][i2]; } else { if (gain[i1][i2] > g_max) g_max = gain[i1][i2]; else if (gain[i1][i2] < g_min) g_min = gain[i1][i2]; } x = Complex.angle(g) * uc; if (i2 > 0) { while (Math.abs(phase[i1][i2-1]-x) > 180.0) { if (x-phase[i1][i2-1] > 180.0) x -= 360.0; else { if (x-phase[i1][i2-1] < -180.0) x += 360.0; } } } phase[i1][i2] = x; ta.append(" " + x + "\n"); if (i1 == 0 && i2 == 0) { p_min = phase[i1][i2]; p_max = phase[i1][i2]; } else { if (phase[i1][i2] > p_max) p_max = phase[i1][i2]; else if (phase[i1][i2] < p_min) p_min = phase[i1][i2]; } // 次の周波数 ff += h; } } // グラフの描画 // グラフ,x軸,及び,y軸のタイトル String title1[] = new String [3]; title1[0] = "ゲイン線図"; title1[1] = "角周波数"; title1[2] = "ゲイン(dB)"; // ゲイン線図 // x軸目盛り double x_scale1[] = new double[3]; x_scale1[0] = fl; // 最小値 x_scale1[1] = fu; // 最大値 x_scale1[2] = 1.0; // 最大値 // y軸目盛り double y_scale1[] = new double[3]; if ((g_max-g_min) < 1.0e-5) { if (g_min > 0.0) { k1 = (int)Math.round(g_min / 20); y_scale1[0] = 20.0 * k1 - 20.0; // 最小値 y_scale1[1] = 20.0 * k1 + 20.0; // 最大値 } else { k1 = (int)Math.round(-g_min / 20); y_scale1[0] = -20.0 * k1 - 20.0; // 最小値 y_scale1[1] = -20.0 * k1 + 20.0; // 最大値 } } else { if (g_min > 0.0) { k1 = (int)(g_min / 20); y_scale1[0] = 20.0 * k1; // 最小値 } else { k1 = (int)(-g_min / 20); if (Math.abs(-20.0*k1-g_min) > 1.0e-5) k1++; y_scale1[0] = -20.0 * k1; // 最小値 } if (g_max > 0.0) { k1 = (int)(g_max / 20); if (Math.abs(20.0*k1-g_max) > 1.0e-5) k1++; y_scale1[1] = 20.0 * k1; // 最大値 } else { k1 = (int)(-g_max / 20); y_scale1[1] = -20.0 * k1; // 最大値 } } y_scale1[2] = 20.0; // 刻み幅 // x軸の小数点以下桁数 p = 0; if (fl < 1.0) { p = 1; x = 0.1; while (fl < x-1.0e-10) { x /= 10.0; p++; } } Bode gp1 = new Bode(title1, g_title, x_scale1, p, y_scale1, 0, freq1, gain, true, true); // 位相線図 // グラフ,x軸,及び,y軸のタイトル String title2[] = new String [3]; title2[0] = "位相線図"; title2[1] = "角周波数"; title2[2] = "位相(度)"; // x軸目盛り double x_scale2[] = new double[3]; x_scale2[0] = fl; // 最小値 x_scale2[1] = fu; // 最大値 x_scale2[2] = 1.0; // 最大値 // y軸目盛り double y_scale2[] = new double[3]; if ((p_max-p_min) < 1.0e-5) { if (p_min > 0.0) { k1 = (int)Math.round(p_min / 90); y_scale2[0] = 90.0 * k1 - 90.0; // 最小値 y_scale2[1] = 90.0 * k1 + 90.0; // 最大値 } else { k1 = (int)Math.round(-p_min / 90); y_scale2[0] = -90.0 * k1 - 90.0; // 最小値 y_scale2[1] = -90.0 * k1 + 90.0; // 最大値 } } else { if (p_min > 0.0) { k1 = (int)(p_min / 90); y_scale2[0] = 90.0 * k1; // 最小値 } else { k1 = (int)(-p_min / 90); if (Math.abs(-90.0*k1-p_min) > 1.0e-5) k1++; y_scale2[0] = -90.0 * k1; // 最小値 } if (p_max > 0.0) { k1 = (int)(p_max / 90); if (Math.abs(90.0*k1-p_max) > 1.0e-5) k1++; y_scale2[1] = 90.0 * k1; // 最大値 } else { k1 = (int)(-p_max / 90); y_scale2[1] = -90.0 * k1; // 最大値 } } y_scale2[2] = 90.0; // 刻み幅 Bode gp2 = new Bode(title2, g_title, x_scale2, p, y_scale2, 0, freq2, phase, true, true); } } /************************************/ /* パラメータが変更されたときの処理 */ /************************************/ public void textValueChanged(TextEvent e) { int i1; try { if (e.getSource() == n_g_t) { n_g = Integer.parseInt(n_g_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (n_g <= 0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } else { si = new double [n_g][]; bo = new double [n_g][]; m = new int [n_g]; n = new int [n_g]; for (i1 = 0; i1 < n_g; i1++) { m[i1] = -1; n[i1] = -1; } remove(sp); sp = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); add(sp, BorderLayout.CENTER); Panel pn2 = new Panel(); pn2.setLayout(new GridLayout(n_g, 1, 10, 10)); sp.add(pn2); ScrollPane sp1[] = new ScrollPane [n_g]; ScrollPane sp2[] = new ScrollPane [n_g]; Panel pn3[] = new Panel [n_g]; Panel pn4[] = new Panel [n_g]; Panel pn5[] = new Panel [n_g]; ex = new TextField [n_g]; bunsi = new Coef [n_g]; bunbo = new Coef [n_g]; for (i1 = 0; i1 < n_g; i1++) { pn3[i1] = new Panel(); pn3[i1].setBackground(new Color(255, 255, 225)); pn3[i1].setLayout(new BorderLayout(5, 10)); pn2.add(pn3[i1]); pn4[i1] = new Panel(); pn4[i1].add(new Label((i1+1) + "番目の式の説明:")); ex[i1] = new TextField(10); pn4[i1].add(ex[i1]); pn3[i1].add(pn4[i1], BorderLayout.NORTH); pn5[i1] = new Panel(); pn5[i1].setLayout(new GridLayout(1, 2, 10, 10)); pn3[i1].add(pn5[i1], BorderLayout.CENTER); sp1[i1] = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); pn5[i1].add(sp1[i1]); bunsi[i1] = new Coef(0, this, i1); sp1[i1].add(bunsi[i1]); sp2[i1] = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); pn5[i1].add(sp2[i1]); bunbo[i1] = new Coef(1, this, i1); sp2[i1].add(bunbo[i1]); } validate(); } } else if (e.getSource() == fl_t) { fl = Double.parseDouble(fl_t.getText()); fu = Double.parseDouble(fu_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (fl <= 0.0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } else if (fu <= fl) { ta.setForeground(Color.red); ta.setText(" 上限より小さい数値を入力してください"); } } else if (e.getSource() == fu_t) { fl = Double.parseDouble(fl_t.getText()); fu = Double.parseDouble(fu_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (fu <= 0.0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } else if (fu <= fl) { ta.setForeground(Color.red); ta.setText(" 下限より大きい数値を入力してください"); } } else if (e.getSource() == k_t) { k = Integer.parseInt(k_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (k <= 0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } } } catch (NumberFormatException em) {} } /************/ /* 終了処理 */ /************/ class WinEnd extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } } /****************************/ /* ボード線図の描画 */ /* coded by Y.Suganuma */ /****************************/ class Bode extends Frame { String title[]; // グラフのタイトル String g_title[]; // 凡例(グラフの内容) double xx_scale[]; // y軸目盛り double x_scale[]; // 元のy軸目盛り double y_scale[]; // y軸目盛り double data_x[][]; // 元のデータ double data_xx[][], data_y[][]; // データ boolean d_t; // タイトル表示の有無 boolean d_g; // 凡例表示の有無 boolean log_c = false; // 対数に変換したか否か int place_x; // 小数点以下の桁数(x軸) int place_y; // 小数点以下の桁数(y軸) int width = 900, height = 600; // Windowの大きさ(初期サイズ) int bx1, bx2, by1, by2; // 表示切り替えボタンの位置 String change = " 色 "; // 表示切り替えボタン float line_w = 1.0f; // 折れ線グラフ等の線の太さ Color cl[] = {Color.black, Color.magenta, Color.blue, Color.orange, Color.cyan, Color.pink, Color.green, Color.yellow, Color.darkGray, Color.red}; // グラフの色 int n_g; // グラフの数 /*********************************************************/ /* コンストラクタ */ /* title_i : グラフ,x軸,及び,y軸のタイトル */ /* g_title_i : 凡例 */ /* x_scale_i : データの最小値,最大値,目盛幅(y) */ /* place_x_i : 小数点以下の桁数(x軸) */ /* y_scale_i : データの最小値,最大値,目盛幅(y) */ /* place_y_i : 小数点以下の桁数(y軸) */ /* data_x_i : グラフのデータ(x軸) */ /* data_y_i : グラフのデータ(y軸) */ /* d_t_i : タイトル表示の有無 */ /* d_g_i : 凡例表示の有無 */ /*********************************************************/ Bode(String title_i[], String g_title_i[], double x_scale_i[], int place_x_i, double y_scale_i[], int place_y_i, double data_x_i[][], double data_y_i[][], boolean d_t_i, boolean d_g_i) { // Frameクラスのコンストラクタの呼び出し super("ボード線図"); // テーブルデータの保存 title = title_i; g_title = g_title_i; x_scale = x_scale_i; place_x = place_x_i; y_scale = y_scale_i; place_y = place_y_i; data_x = data_x_i; data_y = data_y_i; d_t = d_t_i; d_g = d_g_i; int i1, i2; int n_g = g_title.length; int n_p = data_x[0].length; xx_scale = new double [3]; data_xx = new double [n_g][n_p]; xx_scale[0] = x_scale[0]; xx_scale[1] = x_scale[1]; for (i1 = 0; i1 < n_g; i1++) { for (i2 = 0; i2 < n_p; i2++) data_xx[i1][i2] = data_x[i1][i2]; } // Windowサイズと表示位置を設定 setSize(width, height); Toolkit tool = getToolkit(); Dimension d = tool.getScreenSize(); setLocation(d.width / 2 - width / 2, d.height / 2 - height / 2); // ウィンドウを表示 setVisible(true); // イベントアダプタ addWindowListener(new WinEnd()); addComponentListener(new ComponentResize()); addMouseListener(new ClickMouse(this)); } /********/ /* 描画 */ /********/ public void paint (Graphics g) { double r, x1, y1, y2, sp, x_scale_org = 0.0; int i1, i2, cr, k, k_x, k_y, k1, k2, kx, kx1, ky, ky1, han, len; int x_l, x_r, y_u, y_d; // 描画領域 int f_size; // フォントサイズ int n_p; // データの数 String s1; Font f; FontMetrics fm; Graphics2D g2 = (Graphics2D)g; // // Windowサイズの取得 // Insets insets = getInsets(); Dimension d = getSize(); width = d.width - (insets.left + insets.right); height = d.height - (insets.top + insets.bottom); x_l = insets.left + 10; x_r = d.width - insets.right - 10; y_u = insets.top + 20; y_d = d.height - insets.bottom; // // グラフタイトルの表示 // r = 0.05; // タイトル領域の割合 f_size = ((y_d - y_u) < (x_r - x_l)) ? (int)((y_d - y_u) * r) : (int)((x_r - x_l) * r); if (f_size < 5) f_size = 5; if (d_t) { f = new Font("TimesRoman", Font.BOLD, f_size); g.setFont(f); fm = g.getFontMetrics(f); len = fm.stringWidth(title[0]); g.drawString(title[0], (x_l+x_r)/2-len/2, y_d-f_size/2); y_d -= f_size; } // // 表示切り替えボタンの設置 // f_size = (int)(0.8 * f_size); if (f_size < 5) f_size = 5; f = new Font("TimesRoman", Font.PLAIN, f_size); fm = g.getFontMetrics(f); g.setFont(f); g.setColor(Color.yellow); len = fm.stringWidth(change); bx1 = x_r - len - 7 * f_size / 10; by1 = y_u - f_size / 2; bx2 = bx1 + len + f_size / 2; by2 = by1 + 6 * f_size / 5; g.fill3DRect(bx1, by1, len+f_size/2, 6*f_size/5, true); g.setColor(Color.black); g.drawString(change, x_r-len-f_size/2, y_u+f_size/2); // // 凡例の表示 // n_g = g_title.length; if (d_g) { han = 0; for (i1 = 0; i1 < n_g; i1++) { len = fm.stringWidth(g_title[i1]); if (len > han) han = len; } han += 15; r = 0.2; // 凡例領域の割合 k1 = (int)((x_r - x_l) * r); if (han > k1) han = k1; kx = x_r - han; ky = y_u + 3 * f_size / 2; k = 0; g2.setStroke(new BasicStroke(7.0f)); for (i1 = 0; i1 < n_g; i1++) { g.setColor(cl[k]); g.drawLine(kx, ky, kx+10, ky); g.setColor(Color.black); g.drawString(g_title[i1], kx+15, ky+2*f_size/5); k++; if (k >= cl.length) k = 0; ky += f_size; } g2.setStroke(new BasicStroke(1.0f)); x_r -= (han + 10); } else x_r -= (int)(0.03 * (x_r - x_l)); // // x軸の対数 // n_p = data_x[0].length; x_scale_org = x_scale[0]; xx_scale[0] = Math.log(x_scale[0]) / Math.log(10.0); xx_scale[1] = Math.log(x_scale[1]) / Math.log(10.0); xx_scale[2] = 1.0; for (i1 = 0; i1 < n_g; i1++) { for (i2 = 0; i2 < n_p; i2++) data_xx[i1][i2] = Math.log(data_x[i1][i2]) / Math.log(10.0); } // // x軸及びy軸のタイトルの表示 // if (title[1].length() > 0 && !title[1].equals("-")) { len = fm.stringWidth(title[1]); g.drawString(title[1], (x_l+x_r)/2-len/2, y_d-4*f_size/5); y_d -= 7 * f_size / 4; } else y_d -= f_size / 2; if (title[2].length() > 0 && !title[2].equals("-")) { g.drawString(title[2], x_l, y_u+f_size/2); y_u += f_size; } // // x軸,y軸,及び,各軸の目盛り // f_size = (int)(0.8 * f_size); if (f_size < 5) f_size = 5; f = new Font("TimesRoman", Font.PLAIN, f_size); fm = g.getFontMetrics(f); y_d -= 3 * f_size / 2; k_y = (int)((y_scale[1] - y_scale[0]) / (0.99 * y_scale[2])); k_x = (int)((xx_scale[1] - xx_scale[0]) / (0.99 * xx_scale[2])); g.setFont(f); DecimalFormat df_x, df_y; df_x = new DecimalFormat("#"); df_y = new DecimalFormat("#"); if (place_x != 0) { s1 = "0."; for (i1 = 0; i1 < place_x; i1++) s1 += "0"; df_x = new DecimalFormat(s1); } if (place_y != 0) { s1 = "#."; for (i1 = 0; i1 < place_y; i1++) s1 += "0"; df_y = new DecimalFormat(s1); } // y軸 y1 = y_scale[0]; len = 0; for (i1 = 0; i1 < k_y+1; i1++) { s1 = df_y.format(y1); k1 = fm.stringWidth(s1); if (k1 > len) len = k1; y1 += y_scale[2]; } g.drawLine(x_l+len+5, y_u, x_l+len+5, y_d); g.drawLine(x_r, y_u, x_r, y_d); y1 = y_scale[0]; x1 = y_d; sp = (double)(y_d - y_u) / k_y; for (i1 = 0; i1 < k_y+1; i1++) { ky = (int)Math.round(x1); s1 = df_y.format(y1); k1 = fm.stringWidth(s1); g.drawString(s1, x_l+len-k1, ky+f_size/2); g.drawLine(x_l+len+5, ky, x_r, ky); y1 += y_scale[2]; x1 -= sp; } x_l += (len + 5); // x軸 x1 = x_scale_org; y1 = x_l; sp = (double)(x_r - x_l) / k_x; for (i1 = 0; i1 < k_x+1; i1++) { kx = (int)Math.round(y1); s1 = df_x.format(x1); k1 = fm.stringWidth(s1); g.drawString(s1, kx-k1/2, y_d+6*f_size/5); g.drawLine(kx, y_d, kx, y_u); if (i1 != k_x) { g.setColor(Color.darkGray); for (i2 = 2; i2 <= 9; i2++) { y2 = Math.log(x1 * i2) / Math.log(10.0); kx = x_l + (int)Math.round(((x_r - x_l) * (y2 - xx_scale[0]) / (xx_scale[1] - xx_scale[0]))); g.drawLine(kx, y_d, kx, y_u); } g.setColor(Color.black); } x1 *= 10.0; y1 += sp; } // // グラフの表示 // g2.setStroke(new BasicStroke(line_w)); k1 = 0; for (i1 = 0; i1 < n_g; i1++) { g.setColor(cl[k1]); kx1 = 0; ky1 = 0; for (i2 = 0; i2 < n_p; i2++) { kx = x_l + (int)((x_r - x_l) * (data_xx[i1][i2] - xx_scale[0]) / (xx_scale[1] - xx_scale[0])); ky = y_d - (int)((y_d - y_u) * (data_y[i1][i2] - y_scale[0]) / (y_scale[1] - y_scale[0])); if (i2 > 0) g.drawLine(kx1, ky1, kx, ky); kx1 = kx; ky1 = ky; } k1++; if (k1 >= cl.length) k1 = 0; } g2.setStroke(new BasicStroke(1.0f)); } /**********************/ /* Windowのサイズ変化 */ /**********************/ class ComponentResize extends ComponentAdapter { public void componentResized(ComponentEvent e) { repaint(); } } /************/ /* 終了処理 */ /************/ class WinEnd extends WindowAdapter { public void windowClosing(WindowEvent e) { setVisible(false); } } /************************************/ /* マウスがクリックされたときの処理 */ /************************************/ class ClickMouse extends MouseAdapter { Bode dd; ClickMouse(Bode dd1) { dd = dd1; } public void mouseClicked(MouseEvent e) { int xp = e.getX(); int yp = e.getY(); // グラフの色,線の太さ等 if (xp > bx1 && xp < bx2 && yp > by1 && yp < by2) { Modify md = new Modify(dd); md.setVisible(true); } } } } /****************************/ /* 色及び線の太さの変更 */ /* coded by Y.Suganuma */ /****************************/ class Modify extends Dialog implements ActionListener, TextListener { Bode dd; // ボード線図 Button bt_dd; TextField rgb[]; TextField r[]; TextField g[]; TextField b[]; TextField tx; Checkbox r1, r2; float line_w = 1.0f; // 折れ線グラフ等の線の太さ Color cl[]; // グラフの色 int n_g; // グラフの数 int wd; // 線の太さを変更するか int n; Panel jp[]; // ボード線図 Modify(Bode dd1) { super(dd1, "色と線の変更", true); // 初期設定 Font f = new Font("TimesRoman", Font.BOLD, 20); setFont(f); dd = dd1; wd = 1; n_g = dd.n_g; if (n_g > 10) n_g = 10; n = n_g + 2; line_w = dd.line_w; cl = new Color[n_g]; for (int i1 = 0; i1 < n_g; i1++) cl[i1] = dd.cl[i1]; set(); // ボタン bt_dd = new Button("OK"); bt_dd.addActionListener(this); jp[n-1].add(bt_dd); } // 設定 void set() { setSize(450, 60*(n)); setBackground(Color.white); setLayout(new GridLayout(n, 1, 5, 5)); jp = new Panel[n]; for (int i1 = 0; i1 < n; i1++) { jp[i1] = new Panel(); add(jp[i1]); } // 色の変更 Label lb[][] = new Label[n_g][3]; rgb = new TextField[n_g]; r = new TextField[n_g]; g = new TextField[n_g]; b = new TextField[n_g]; for (int i1 = 0; i1 < n_g; i1++) { rgb[i1] = new TextField(3); rgb[i1].setBackground(new Color(cl[i1].getRed(), cl[i1].getGreen(), cl[i1].getBlue())); jp[i1].add(rgb[i1]); lb[i1][0] = new Label(" 赤"); jp[i1].add(lb[i1][0]); r[i1] = new TextField(3); r[i1].setBackground(Color.white); r[i1].setText(Integer.toString(cl[i1].getRed())); r[i1].addTextListener(this); jp[i1].add(r[i1]); lb[i1][1] = new Label("緑"); jp[i1].add(lb[i1][1]); g[i1] = new TextField(3); g[i1].setBackground(Color.white); g[i1].setText(Integer.toString(cl[i1].getGreen())); g[i1].addTextListener(this); jp[i1].add(g[i1]); lb[i1][2] = new Label("青"); jp[i1].add(lb[i1][2]); b[i1] = new TextField(3); b[i1].setBackground(Color.white); b[i1].setText(Integer.toString(cl[i1].getBlue())); b[i1].addTextListener(this); jp[i1].add(b[i1]); } // 線の変更 if (wd > 0) { Label lb1 = new Label("線の太さ:"); jp[n_g].add(lb1); tx = new TextField(2); tx.setBackground(Color.white); tx.setText(Integer.toString((int)line_w)); jp[n_g].add(tx); } } // TextFieldの内容が変更されたときの処理 public void textValueChanged(TextEvent e) { for (int i1 = 0; i1 < n_g; i1++) { if (e.getSource() == r[i1] || e.getSource() == g[i1] || e.getSource() == b[i1]) { String str = r[i1].getText(); int rc = str.length()>0 ? Integer.parseInt(str) : 0; str = g[i1].getText(); int gc = str.length()>0 ? Integer.parseInt(str) : 0; str = b[i1].getText(); int bc = str.length()>0 ? Integer.parseInt(str) : 0; rgb[i1].setBackground(new Color(rc, gc, bc)); } } } // 値の設定 public void actionPerformed(ActionEvent e) { for (int i1 = 0; i1 < n_g; i1++) { String str = r[i1].getText(); int rc = str.length()>0 ? Integer.parseInt(str) : 0; str = g[i1].getText(); int gc = str.length()>0 ? Integer.parseInt(str) : 0; str = b[i1].getText(); int bc = str.length()>0 ? Integer.parseInt(str) : 0; dd.cl[i1] = new Color(rc, gc, bc); } dd.line_w = Integer.parseInt(tx.getText()); dd.repaint(); setVisible(false); } } /****************************/ /* クラスCoef */ /* coded by Y.Suganuma */ /****************************/ class Coef extends Panel implements TextListener { int type, no; BodePlot ba; TextField n_t; TextField bun_t[]; Label lb[]; Panel pn; /***************************/ /* コンストラクタ */ /* type_i : =0 : 分子 */ /* =1 : 分母 */ /* ba_i : BodePlot */ /* no_i : 式番号 */ /***************************/ Coef(int type_i, BodePlot ba_i, int no_i) { int i1; type = type_i; ba = ba_i; no = no_i; // 次数 setLayout(new BorderLayout(5, 10)); Panel pn1 = new Panel(); add(pn1, BorderLayout.NORTH); if (type == 0) pn1.add(new Label((no+1)+".分子の次数")); else pn1.add(new Label((no+1)+".分母の次数")); n_t = new TextField(2); n_t.addTextListener(this); pn1.add(n_t); // 係数 pn = new Panel(); add(pn, BorderLayout.CENTER); } /************************************/ /* パラメータが変更されたときの処理 */ /************************************/ public void textValueChanged(TextEvent e) { int i1, n; ba.ta.setForeground(Color.black); ba.ta.setText(""); try { n = Integer.parseInt(n_t.getText()); if (n < 0) { n_t.setText("0"); n = 0; } if (type == 0) { ba.m[no] = n; ba.si[no] = new double [n+1]; } else { ba.n[no] = n; ba.bo[no] = new double [n+1]; } remove(pn); pn = new Panel(); add(pn, BorderLayout.CENTER); pn.setLayout(new GridLayout(n+1, 1, 10, 0)); Panel pnx[] = new Panel [n+1]; bun_t = new TextField [n+1]; lb = new Label [n+1]; pnx[0] = new Panel(); pn.add(pnx[0]); lb[0] = new Label("定数項"); pnx[0].add(lb[0]); bun_t[0] = new TextField(3); pnx[0].add(bun_t[0]); for (i1 = 1; i1 <= n; i1++) { pnx[i1] = new Panel(); pn.add(pnx[i1]); lb[i1] = new Label("s の "+i1+" 次の項"); pnx[i1].add(lb[i1]); bun_t[i1] = new TextField(3); pnx[i1].add(bun_t[i1]); } getParent().validate(); } catch (NumberFormatException em) {} } } /****************************/ /* クラスComplex */ /* coded by Y.Suganuma */ /****************************/ class Complex { private double r; // 実部 private double i; // 虚部 /******************/ /* コンストラクタ */ /* a : 実部 */ /* b : 虚部 */ /******************/ Complex(double a, double b) { r = a; i = b; } /******************/ /* コンストラクタ */ /* a : 実部 */ /******************/ Complex(double a) { r = a; i = 0.0; } /******************/ /* コンストラクタ */ /******************/ Complex() { r = 0.0; i = 0.0; } /********/ /* 出力 */ /********/ void output(PrintStream out, Complex a) { out.print("(" + a.r + ", " + a.i + ")"); } /******************/ /* 複素数の絶対値 */ /******************/ static double abs(Complex a) { double x; x = Math.sqrt(a.r * a.r + a.i * a.i); return x; } /****************/ /* 複素数の角度 */ /****************/ static double angle(Complex a) { double x; if (a.r == 0.0 && a.i == 0.0) x = 0.0; else x = Math.atan2(a.i, a.r); return x; } /****************/ /* 複素数のn乗 */ /****************/ static Complex pow(Complex a, int n) { int i1; Complex c = new Complex (1); if (n >= 0) { for (i1 = 0; i1 < n; i1++) c = Complex.mul(c, a); } else { for (i1 = 0; i1 < -n; i1++) c = Complex.mul(c, a); c = Complex.dev(new Complex(1.0), c); } return c; } /****************/ /* 複素数の加算 */ /****************/ static Complex add(Complex a, Complex b) { Complex c = new Complex(); c.r = a.r + b.r; c.i = a.i + b.i; return c; } /****************/ /* 複素数の減算 */ /****************/ static Complex sub(Complex a, Complex b) { Complex c = new Complex(); c.r = a.r - b.r; c.i = a.i - b.i; return c; } /****************/ /* 複素数の乗算 */ /****************/ static Complex mul(Complex a, Complex b) { Complex c = new Complex(); c.r = a.r * b.r - a.i * b.i; c.i = a.i * b.r + a.r * b.i; return c; } /****************/ /* 複素数の除算 */ /****************/ static Complex dev(Complex a, Complex b) { double x; Complex c = new Complex(); x = 1.0 / (b.r * b.r + b.i * b.i); c.r = (a.r * b.r + a.i * b.i) * x; c.i = (a.i * b.r - a.r * b.i) * x; return c; } }
/*****************************************/ /* 伝達関数のゲインと位相の計算(Swing) */ /* coded by Y.Suganuma */ /*****************************************/ import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.io.*; import java.text.*; public class Test { public static void main (String[] args) { BodePlot_s bd = new BodePlot_s("ボード線図"); } } class BodePlot_s extends JFrame implements ActionListener, DocumentListener { double fl, fu, si[][], bo[][]; int k, m[], n[], n_g = -1; JTextField fl_t, fu_t, k_t, n_g_t, ex[]; JTextArea ta; JButton bt; Coef bunsi[], bunbo[]; Container cp; JScrollPane sp; Font f = new Font("TimesRoman", Font.BOLD, 20); /******************/ /* コンストラクタ */ /******************/ BodePlot_s(String name) { // Frameクラスのコンストラクタ(Windowのタイトルを引き渡す) super(name); // Windowの大きさ setSize(790, 590); // レイアウト,背景色,フォント cp = getContentPane(); cp.setLayout(new BorderLayout(5, 5)); cp.setBackground(new Color(225, 255, 225)); // 上のパネル JPanel pn1 = new JPanel(); pn1.setBackground(new Color(225, 255, 225)); pn1.setLayout(new GridLayout(2, 1, 5, 5)); cp.add(pn1, BorderLayout.NORTH); JPanel pn11 = new JPanel(); pn11.setBackground(new Color(225, 255, 225)); pn1.add(pn11); JLabel lb1 = new JLabel("式の数(グラフの数)"); lb1.setFont(f); pn11.add(lb1); n_g_t = new JTextField(3); n_g_t.setFont(f); n_g_t.getDocument().putProperty("name", "no"); n_g_t.getDocument().addDocumentListener(this); pn11.add(n_g_t); pn11.add(new JLabel(" ")); bt = new JButton("実行"); bt.setBackground(Color.pink); bt.setFont(f); bt.addActionListener(this); pn11.add(bt); JPanel pn12 = new JPanel(); pn12.setBackground(new Color(225, 255, 225)); pn1.add(pn12); JLabel lb2 = new JLabel("周波数下限"); lb2.setFont(f); pn12.add(lb2); fl = 0.01; fl_t = new JTextField("0.01", 3); fl_t.setFont(f); fl_t.getDocument().putProperty("name", "lower"); fl_t.getDocument().addDocumentListener(this); pn12.add(fl_t); JLabel lb3 = new JLabel(" 周波数上限"); lb3.setFont(f); pn12.add(lb3); fu = 100.0; fu_t = new JTextField("100", 3); fu_t.setFont(f); fu_t.getDocument().putProperty("name", "upper"); fu_t.getDocument().addDocumentListener(this); pn12.add(fu_t); JLabel lb4 = new JLabel(" データ数"); lb4.setFont(f); pn12.add(lb4); k = 100; k_t = new JTextField("100", 3); k_t.setFont(f); k_t.getDocument().putProperty("name", "data"); k_t.getDocument().addDocumentListener(this); pn12.add(k_t); // 中央のパネル sp = new JScrollPane(); cp.add(sp, BorderLayout.CENTER); // 下のパネル JPanel pn6 = new JPanel(); pn6.setBackground(new Color(225, 255, 225)); cp.add(pn6, BorderLayout.SOUTH); ta = new JTextArea(5, 35); ta.setFont(f); JScrollPane scroll2 = new JScrollPane(ta); pn6.add(scroll2); // ウィンドウを表示 setVisible(true); // イベントアダプタ addWindowListener(new WinEnd()); } /************/ /* log10(x) */ /************/ static double log10(double x) { return Math.log(x) / Math.log(10.0); } /****************************************/ /* 伝達関数のsにjωを代入した値の計算 */ /* ff : ω(周波数) */ /* m : 分子の次数 */ /* si : 分子多項式の係数 */ /* n : 分母の次数 */ /* bo : 分母多項式の係数 */ /* return : 結果 */ /****************************************/ static Complex G_s(double ff, int m, double si[], int n, double bo[]) { Complex f, x, y; // 周波数を複素数に変換 f = new Complex (0.0, ff); // 分子 x = value(f, m, si); // 分母 y = value(f, n, bo); return Complex.dev(x, y); } /**************************************/ /* 多項式のsにjωを代入した値の計算 */ /* f : jω(周波数,複素数) */ /* n : 多項式の次数 */ /* a : 多項式の係数 */ /* return : 結果 */ /**************************************/ static Complex value(Complex f, int n, double a[]) { int i1, k1; Complex x; x = new Complex (0.0, 0.0); for (i1 = 0; i1 <= n; i1++) x = Complex.add(x, Complex.mul(new Complex(a[i1]), Complex.pow(f, i1))); return x; } /******************************/ /* 上,左,下,右の余白の設定 */ /******************************/ public Insets getInsets() { return new Insets(70, 20, 20, 20); } /******************************/ /* ボタンが押されたときの処理 */ /******************************/ public void actionPerformed(ActionEvent e) { double x, h, f, ff, g_min, g_max, p_min, p_max, uc = 90.0 / Math.asin(1.0); int i1, i2, k1, p, sw = 0; String g_title[] = null; Complex g; ta.setForeground(Color.black); ta.setText(""); // エラーの判定 if (n_g < 0) { sw = 1; ta.setForeground(Color.red); ta.append(" 式の数を入力してください\n"); } else { g_title = new String [n_g]; for (i1 = 0; i1 < n_g; i1++) { if (ex[i1].getText().length() <= 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の説明(凡例)を入力してください\n"); } else g_title[i1] = ex[i1].getText(); } for (i1 = 0; i1 < n_g; i1++) { if (m[i1] < 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分子の次数を入力してください\n"); } else { for (i2 = 0; i2 <= m[i1]; i2++) { if (bunsi[i1].bun_t[i2].getText().length() <= 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分子の s の " + i2 + " 次の係数を入力してください\n"); } else si[i1][i2] = Double.parseDouble(bunsi[i1].bun_t[i2].getText()); } } } for (i1 = 0; i1 < n_g; i1++) { if (n[i1] < 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分母の次数を入力してください\n"); } else { for (i2 = 0; i2 <= n[i1]; i2++) { if (bunbo[i1].bun_t[i2].getText().length() <= 0) { sw = 1; ta.setForeground(Color.red); ta.append(" " + (i1+1) + " 番目の式の分母の s の " + i2 + " 次の係数を入力してください\n"); } else bo[i1][i2] = Double.parseDouble(bunbo[i1].bun_t[i2].getText()); } } } } // ゲインと位相の計算 if (sw == 0) { // 下限と上限の再設定 if (fl < 1.0) { x = 0.1; while (fl < x-1.0e-10) x /= 10.0; fl = x; } else { x = 1.0; while (fl > x-1.0e-10) x *= 10.0; fl = x; } if (fu < 1.0) { x = 0.1; while (fu < x+1.0e-10) x /= 10.0; fu = 10.0 * x; } else { x = 1.0; while (fu > x+1.0e-10) x *= 10.0; fu = x; } // 初期設定 double freq1[][] = new double [n_g][k+1]; double freq2[][] = new double [n_g][k+1]; double gain[][] = new double [n_g][k+1]; double phase[][] = new double [n_g][k+1]; h = (log10(fu) - log10(fl)) / k; g_min = 0.0; g_max = 0.0; p_min = 0.0; p_max = 0.0; ta.setText(" 角周波数 ゲイン(dB) 位相(度)\n"); for (i1 = 0; i1 < n_g; i1++) { ff = log10(fl); ta.append(g_title[i1] + "\n"); for (i2 = 0; i2 <= k; i2++) { // 周波数の対数を元に戻す f = Math.pow(10.0, ff); freq1[i1][i2] = f; freq2[i1][i2] = f; ta.append(" " + f); // 値の計算 g = G_s(f, m[i1], si[i1], n[i1], bo[i1]); // ゲインと位相の計算 gain[i1][i2] = 20.0 * log10(Complex.abs(g)); ta.append(" " + gain[i1][i2]); if (i1 == 0 && i2 == 0) { g_min = gain[i1][i2]; g_max = gain[i1][i2]; } else { if (gain[i1][i2] > g_max) g_max = gain[i1][i2]; else if (gain[i1][i2] < g_min) g_min = gain[i1][i2]; } x = Complex.angle(g) * uc; if (i2 > 0) { while (Math.abs(phase[i1][i2-1]-x) > 180.0) { if (x-phase[i1][i2-1] > 180.0) x -= 360.0; else { if (x-phase[i1][i2-1] < -180.0) x += 360.0; } } } phase[i1][i2] = x; ta.append(" " + x + "\n"); if (i1 == 0 && i2 == 0) { p_min = phase[i1][i2]; p_max = phase[i1][i2]; } else { if (phase[i1][i2] > p_max) p_max = phase[i1][i2]; else if (phase[i1][i2] < p_min) p_min = phase[i1][i2]; } // 次の周波数 ff += h; } } // グラフの描画 // グラフ,x軸,及び,y軸のタイトル String title1[] = new String [3]; title1[0] = "ゲイン線図"; title1[1] = "角周波数"; title1[2] = "ゲイン(dB)"; // ゲイン線図 // x軸目盛り double x_scale1[] = new double[3]; x_scale1[0] = fl; // 最小値 x_scale1[1] = fu; // 最大値 x_scale1[2] = 1.0; // 最大値 // y軸目盛り double y_scale1[] = new double[3]; if ((g_max-g_min) < 1.0e-5) { if (g_min > 0.0) { k1 = (int)Math.round(g_min / 20); y_scale1[0] = 20.0 * k1 - 20.0; // 最小値 y_scale1[1] = 20.0 * k1 + 20.0; // 最大値 } else { k1 = (int)Math.round(-g_min / 20); y_scale1[0] = -20.0 * k1 - 20.0; // 最小値 y_scale1[1] = -20.0 * k1 + 20.0; // 最大値 } } else { if (g_min > 0.0) { k1 = (int)(g_min / 20); y_scale1[0] = 20.0 * k1; // 最小値 } else { k1 = (int)(-g_min / 20); if (Math.abs(-20.0*k1-g_min) > 1.0e-5) k1++; y_scale1[0] = -20.0 * k1; // 最小値 } if (g_max > 0.0) { k1 = (int)(g_max / 20); if (Math.abs(20.0*k1-g_max) > 1.0e-5) k1++; y_scale1[1] = 20.0 * k1; // 最大値 } else { k1 = (int)(-g_max / 20); y_scale1[1] = -20.0 * k1; // 最大値 } } y_scale1[2] = 20.0; // 刻み幅 // x軸の小数点以下桁数 p = 0; if (fl < 1.0) { p = 1; x = 0.1; while (fl < x-1.0e-10) { x /= 10.0; p++; } } Bode gp1 = new Bode(title1, g_title, x_scale1, p, y_scale1, 0, freq1, gain, true, true); // 位相線図 // グラフ,x軸,及び,y軸のタイトル String title2[] = new String [3]; title2[0] = "位相線図"; title2[1] = "角周波数"; title2[2] = "位相(度)"; // x軸目盛り double x_scale2[] = new double[3]; x_scale2[0] = fl; // 最小値 x_scale2[1] = fu; // 最大値 x_scale2[2] = 1.0; // 最大値 // y軸目盛り double y_scale2[] = new double[3]; if ((p_max-p_min) < 1.0e-5) { if (p_min > 0.0) { k1 = (int)Math.round(p_min / 90); y_scale2[0] = 90.0 * k1 - 90.0; // 最小値 y_scale2[1] = 90.0 * k1 + 90.0; // 最大値 } else { k1 = (int)Math.round(-p_min / 90); y_scale2[0] = -90.0 * k1 - 90.0; // 最小値 y_scale2[1] = -90.0 * k1 + 90.0; // 最大値 } } else { if (p_min > 0.0) { k1 = (int)(p_min / 90); y_scale2[0] = 90.0 * k1; // 最小値 } else { k1 = (int)(-p_min / 90); if (Math.abs(-90.0*k1-p_min) > 1.0e-5) k1++; y_scale2[0] = -90.0 * k1; // 最小値 } if (p_max > 0.0) { k1 = (int)(p_max / 90); if (Math.abs(90.0*k1-p_max) > 1.0e-5) k1++; y_scale2[1] = 90.0 * k1; // 最大値 } else { k1 = (int)(-p_max / 90); y_scale2[1] = -90.0 * k1; // 最大値 } } y_scale2[2] = 90.0; // 刻み幅 Bode gp2 = new Bode(title2, g_title, x_scale2, p, y_scale2, 0, freq2, phase, true, true); } } /************************************/ /* パラメータが変更されたときの処理 */ /************************************/ public void changedUpdate(DocumentEvent e) {} public void removeUpdate(DocumentEvent e) {} public void insertUpdate(DocumentEvent e) { int i1; String key = e.getDocument().getProperty("name").toString(); try { if (key.equals("no")) { n_g = Integer.parseInt(n_g_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (n_g <= 0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } else { remove(sp); si = new double [n_g][]; bo = new double [n_g][]; m = new int [n_g]; n = new int [n_g]; for (i1 = 0; i1 < n_g; i1++) { m[i1] = -1; n[i1] = -1; } JPanel pn2 = new JPanel(); pn2.setBackground(new Color(225, 255, 225)); pn2.setLayout(new GridLayout(n_g, 1, 5, 5)); sp = new JScrollPane(pn2); JScrollPane sp1[] = new JScrollPane [n_g]; JScrollPane sp2[] = new JScrollPane [n_g]; JPanel pn3[] = new JPanel [n_g]; JPanel pn4[] = new JPanel [n_g]; JPanel pn5[] = new JPanel [n_g]; JLabel lb[] = new JLabel [n_g]; ex = new JTextField [n_g]; bunsi = new Coef [n_g]; bunbo = new Coef [n_g]; for (i1 = 0; i1 < n_g; i1++) { pn3[i1] = new JPanel(); pn3[i1].setBackground(new Color(255, 255, 225)); pn3[i1].setLayout(new BorderLayout(5, 5)); pn2.add(pn3[i1]); pn4[i1] = new JPanel(); pn4[i1].setBackground(Color.white); lb[i1] = new JLabel((i1+1) + "番目の式の説明:"); lb[i1].setFont(f); pn4[i1].add(lb[i1]); ex[i1] = new JTextField(10); ex[i1].setFont(f); pn4[i1].add(ex[i1]); pn3[i1].add(pn4[i1], BorderLayout.NORTH); pn5[i1] = new JPanel(); pn5[i1].setBackground(Color.white); pn5[i1].setLayout(new GridLayout(1, 2, 5, 5)); bunsi[i1] = new Coef(0, this, i1); sp1[i1] = new JScrollPane(bunsi[i1]); pn5[i1].add(sp1[i1]); bunbo[i1] = new Coef(1, this, i1); sp2[i1] = new JScrollPane(bunbo[i1]); pn5[i1].add(sp2[i1]); pn3[i1].add(pn5[i1], BorderLayout.CENTER); } cp.add(sp, BorderLayout.CENTER); validate(); } } else if (key.equals("lower")) { fl = Double.parseDouble(fl_t.getText()); fu = Double.parseDouble(fu_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (fl <= 0.0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } else if (fu <= fl) { ta.setForeground(Color.red); ta.setText(" 上限より小さい数値を入力してください"); } } else if (key.equals("upper")) { fl = Double.parseDouble(fl_t.getText()); fu = Double.parseDouble(fu_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (fu <= 0.0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } else if (fu <= fl) { ta.setForeground(Color.red); ta.setText(" 下限より大きい数値を入力してください"); } } else if (key.equals("data")) { k = Integer.parseInt(k_t.getText()); ta.setForeground(Color.black); ta.setText(""); if (k <= 0) { ta.setForeground(Color.red); ta.setText(" 0 より大きい数値を入力してください"); } } } catch (NumberFormatException em) {} } /************/ /* 終了処理 */ /************/ class WinEnd extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } } /****************************/ /* ボード線図の描画 */ /* coded by Y.Suganuma */ /****************************/ class Bode extends JFrame { Draw_bode pn; /*********************************************************/ /* コンストラクタ */ /* title_i : グラフ,x軸,及び,y軸のタイトル */ /* g_title_i : 凡例 */ /* x_scale_i : データの最小値,最大値,目盛幅(y) */ /* place_x_i : 小数点以下の桁数(x軸) */ /* y_scale_i : データの最小値,最大値,目盛幅(y) */ /* place_y_i : 小数点以下の桁数(y軸) */ /* data_x_i : グラフのデータ(x軸) */ /* data_y_i : グラフのデータ(y軸) */ /* d_t_i : タイトル表示の有無 */ /* d_g_i : 凡例表示の有無 */ /*********************************************************/ Bode(String title_i[], String g_title_i[], double x_scale_i[], int place_x_i, double y_scale_i[], int place_y_i, double data_x_i[][], double data_y_i[][], boolean d_t_i, boolean d_g_i) { // JFrameクラスのコンストラクタの呼び出し super("ボード線図"); // Windowサイズと表示位置を設定 int width = 900, height = 600; // Windowの大きさ(初期サイズ) setSize(width, height); Toolkit tool = getToolkit(); Dimension d = tool.getScreenSize(); setLocation(d.width / 2 - width / 2, d.height / 2 - height / 2); // 描画パネル Container cp = getContentPane(); pn = new Draw_bode(title_i, g_title_i, x_scale_i, place_x_i, y_scale_i, place_y_i, data_x_i, data_y_i, d_t_i, d_g_i, this); cp.add(pn); // ウィンドウを表示 setVisible(true); // イベントアダプタ addWindowListener(new WinEnd()); addComponentListener(new ComponentResize()); } /**********************/ /* Windowのサイズ変化 */ /**********************/ class ComponentResize extends ComponentAdapter { public void componentResized(ComponentEvent e) { pn.repaint(); } } /************/ /* 終了処理 */ /************/ class WinEnd extends WindowAdapter { public void windowClosing(WindowEvent e) { setVisible(false); } } } class Draw_bode extends JPanel { String title[]; // グラフのタイトル String g_title[]; // 凡例(グラフの内容) double xx_scale[]; // y軸目盛り double x_scale[]; // 元のy軸目盛り double y_scale[]; // y軸目盛り double data_x[][]; // 元のデータ double data_xx[][], data_y[][]; // データ boolean d_t; // タイトル表示の有無 boolean d_g; // 凡例表示の有無 boolean log_c = false; // 対数に変換したか否か int place_x; // 小数点以下の桁数(x軸) int place_y; // 小数点以下の桁数(y軸) int width = 900, height = 600; // Windowの大きさ(初期サイズ) int bx1, bx2, by1, by2; // 表示切り替えボタンの位置 Bode bd; String change = " 色 "; // 表示切り替えボタン float line_w = 1.0f; // 折れ線グラフ等の線の太さ Color cl[] = {Color.black, Color.magenta, Color.blue, Color.orange, Color.cyan, Color.pink, Color.green, Color.yellow, Color.darkGray, Color.red}; // グラフの色 int n_g; // グラフの数 /******************/ /* コンストラクタ */ /******************/ Draw_bode(String title_i[], String g_title_i[], double x_scale_i[], int place_x_i, double y_scale_i[], int place_y_i, double data_x_i[][], double data_y_i[][], boolean d_t_i, boolean d_g_i, Bode bd1) { // 背景色 setBackground(Color.white); // テーブルデータの保存 title = title_i; g_title = g_title_i; x_scale = x_scale_i; place_x = place_x_i; y_scale = y_scale_i; place_y = place_y_i; data_x = data_x_i; data_y = data_y_i; d_t = d_t_i; d_g = d_g_i; bd = bd1; int i1, i2; int n_g = g_title.length; int n_p = data_x[0].length; xx_scale = new double [3]; data_xx = new double [n_g][n_p]; xx_scale[0] = x_scale[0]; xx_scale[1] = x_scale[1]; for (i1 = 0; i1 < n_g; i1++) { for (i2 = 0; i2 < n_p; i2++) data_xx[i1][i2] = data_x[i1][i2]; } // イベントアダプタ addMouseListener(new ClickMouse(this)); } /********/ /* 描画 */ /********/ public void paintComponent (Graphics g) { super.paintComponent(g); // 親クラスの描画(必ず必要) double r, x1, y1, y2, sp, x_scale_org = 0.0; int i1, i2, k, k_x, k_y, k1, k2, kx, kx1, ky, ky1, han, len; int x_l, x_r, y_u, y_d; // 描画領域 int f_size; // フォントサイズ int n_p; // データの数 String s1; Font f; FontMetrics fm; Graphics2D g2 = (Graphics2D)g; // // Windowサイズの取得 // Insets insets = bd.getInsets(); Dimension d = bd.getSize(); width = d.width - (insets.left + insets.right); height = d.height - (insets.top + insets.bottom); x_l = insets.left + 10; x_r = d.width - insets.right - 10; y_u = 20; y_d = d.height - insets.bottom - insets.top; // // グラフタイトルの表示 // r = 0.05; // タイトル領域の割合 f_size = ((y_d - y_u) < (x_r - x_l)) ? (int)((y_d - y_u) * r) : (int)((x_r - x_l) * r); if (f_size < 5) f_size = 5; if (d_t) { f = new Font("TimesRoman", Font.BOLD, f_size); g.setFont(f); fm = g.getFontMetrics(f); len = fm.stringWidth(title[0]); g.drawString(title[0], (x_l+x_r)/2-len/2, y_d-f_size/2); y_d -= f_size; } // // 表示切り替えボタンの設置 // f_size = (int)(0.8 * f_size); if (f_size < 5) f_size = 5; f = new Font("TimesRoman", Font.PLAIN, f_size); fm = g.getFontMetrics(f); g.setFont(f); g.setColor(Color.yellow); len = fm.stringWidth(change); bx1 = x_r - len - 7 * f_size / 10; by1 = y_u - f_size / 2; bx2 = bx1 + len + f_size / 2; by2 = by1 + 6 * f_size / 5; g.fill3DRect(bx1, by1, len+f_size/2, 6*f_size/5, true); g.setColor(Color.black); g.drawString(change, x_r-len-f_size/2, y_u+f_size/2); // // 凡例の表示 // n_g = g_title.length; if (d_g) { han = 0; for (i1 = 0; i1 < n_g; i1++) { len = fm.stringWidth(g_title[i1]); if (len > han) han = len; } han += 15; r = 0.2; // 凡例領域の割合 k1 = (int)((x_r - x_l) * r); if (han > k1) han = k1; kx = x_r - han; ky = y_u + 3 * f_size / 2; k = 0; g2.setStroke(new BasicStroke(7.0f)); for (i1 = 0; i1 < n_g; i1++) { g.setColor(cl[k]); g.drawLine(kx, ky, kx+10, ky); g.setColor(Color.black); g.drawString(g_title[i1], kx+15, ky+2*f_size/5); k++; if (k >= cl.length) k = 0; ky += f_size; } g2.setStroke(new BasicStroke(1.0f)); x_r -= (han + 10); } else x_r -= (int)(0.03 * (x_r - x_l)); // // x軸の対数 // n_p = data_x[0].length; x_scale_org = x_scale[0]; xx_scale[0] = Math.log(x_scale[0]) / Math.log(10.0); xx_scale[1] = Math.log(x_scale[1]) / Math.log(10.0); xx_scale[2] = 1.0; for (i1 = 0; i1 < n_g; i1++) { for (i2 = 0; i2 < n_p; i2++) data_xx[i1][i2] = Math.log(data_x[i1][i2]) / Math.log(10.0); } // // x軸及びy軸のタイトルの表示 // if (title[1].length() > 0 && !title[1].equals("-")) { len = fm.stringWidth(title[1]); g.drawString(title[1], (x_l+x_r)/2-len/2, y_d-4*f_size/5); y_d -= 7 * f_size / 4; } else y_d -= f_size / 2; if (title[2].length() > 0 && !title[2].equals("-")) { g.drawString(title[2], x_l, y_u+f_size/2); y_u += f_size; } // // x軸,y軸,及び,各軸の目盛り // f_size = (int)(0.8 * f_size); if (f_size < 5) f_size = 5; f = new Font("TimesRoman", Font.PLAIN, f_size); fm = g.getFontMetrics(f); y_d -= 3 * f_size / 2; k_y = (int)((y_scale[1] - y_scale[0]) / (0.99 * y_scale[2])); k_x = (int)((xx_scale[1] - xx_scale[0]) / (0.99 * xx_scale[2])); g.setFont(f); DecimalFormat df_x, df_y; df_x = new DecimalFormat("#"); df_y = new DecimalFormat("#"); if (place_x != 0) { s1 = "0."; for (i1 = 0; i1 < place_x; i1++) s1 += "0"; df_x = new DecimalFormat(s1); } if (place_y != 0) { s1 = "#."; for (i1 = 0; i1 < place_y; i1++) s1 += "0"; df_y = new DecimalFormat(s1); } // y軸 y1 = y_scale[0]; len = 0; for (i1 = 0; i1 < k_y+1; i1++) { s1 = df_y.format(y1); k1 = fm.stringWidth(s1); if (k1 > len) len = k1; y1 += y_scale[2]; } g.drawLine(x_l+len+5, y_u, x_l+len+5, y_d); g.drawLine(x_r, y_u, x_r, y_d); y1 = y_scale[0]; x1 = y_d; sp = (double)(y_d - y_u) / k_y; for (i1 = 0; i1 < k_y+1; i1++) { ky = (int)Math.round(x1); s1 = df_y.format(y1); k1 = fm.stringWidth(s1); g.drawString(s1, x_l+len-k1, ky+f_size/2); g.drawLine(x_l+len+5, ky, x_r, ky); y1 += y_scale[2]; x1 -= sp; } x_l += (len + 5); // x軸 x1 = x_scale_org; y1 = x_l; sp = (double)(x_r - x_l) / k_x; for (i1 = 0; i1 < k_x+1; i1++) { kx = (int)Math.round(y1); s1 = df_x.format(x1); k1 = fm.stringWidth(s1); g.drawString(s1, kx-k1/2, y_d+6*f_size/5); g.drawLine(kx, y_d, kx, y_u); if (i1 != k_x) { g.setColor(Color.darkGray); for (i2 = 2; i2 <= 9; i2++) { y2 = Math.log(x1 * i2) / Math.log(10.0); kx = x_l + (int)Math.round(((x_r - x_l) * (y2 - xx_scale[0]) / (xx_scale[1] - xx_scale[0]))); g.drawLine(kx, y_d, kx, y_u); } g.setColor(Color.black); } x1 *= 10.0; y1 += sp; } // // グラフの表示 // g2.setStroke(new BasicStroke(line_w)); k1 = 0; for (i1 = 0; i1 < n_g; i1++) { g.setColor(cl[k1]); kx1 = 0; ky1 = 0; for (i2 = 0; i2 < n_p; i2++) { kx = x_l + (int)((x_r - x_l) * (data_xx[i1][i2] - xx_scale[0]) / (xx_scale[1] - xx_scale[0])); ky = y_d - (int)((y_d - y_u) * (data_y[i1][i2] - y_scale[0]) / (y_scale[1] - y_scale[0])); if (i2 > 0) g.drawLine(kx1, ky1, kx, ky); kx1 = kx; ky1 = ky; } k1++; if (k1 >= cl.length) k1 = 0; } g2.setStroke(new BasicStroke(1.0f)); } /************************************/ /* マウスがクリックされたときの処理 */ /************************************/ class ClickMouse extends MouseAdapter { Draw_bode dd; ClickMouse(Draw_bode dd1) { dd = dd1; } public void mouseClicked(MouseEvent e) { int xp = e.getX(); int yp = e.getY(); // グラフの色,線の太さ等 if (xp > bx1 && xp < bx2 && yp > by1 && yp < by2) { Modify md = new Modify(dd.bd, dd); md.setVisible(true); } } } } /****************************/ /* 色及び線の太さの変更 */ /* coded by Y.Suganuma */ /****************************/ class Modify extends JDialog implements ActionListener, TextListener { Draw_bode dd; // ボード線図 JButton bt_dd; TextField rgb[]; TextField r[]; TextField g[]; TextField b[]; JTextField tx; JRadioButton r1, r2; float line_w = 1.0f; // 折れ線グラフ等の線の太さ Color cl[]; // グラフの色 int n_g; // グラフの数 int wd; // 線の太さを変更するか int n; JPanel jp[]; // ボード線図 Modify(Frame host, Draw_bode dd1) { super(host, "色と線の変更", true); // 初期設定 dd = dd1; wd = 1; n_g = dd.n_g; if (n_g > 10) n_g = 10; n = n_g + 2; line_w = dd.line_w; cl = new Color[n_g]; for (int i1 = 0; i1 < n_g; i1++) cl[i1] = dd.cl[i1]; set(); // ボタン Font f = new Font("TimesRoman", Font.BOLD, 20); bt_dd = new JButton("OK"); bt_dd.setFont(f); bt_dd.addActionListener(this); jp[n-1].add(bt_dd); } // 設定 void set() { setSize(450, 60*(n)); Container cp = getContentPane(); cp.setBackground(Color.white); cp.setLayout(new GridLayout(n, 1, 5, 5)); jp = new JPanel[n]; for (int i1 = 0; i1 < n; i1++) { jp[i1] = new JPanel(); cp.add(jp[i1]); } Font f = new Font("TimesRoman", Font.BOLD, 20); // 色の変更 JLabel lb[][] = new JLabel[n_g][3]; rgb = new TextField[n_g]; r = new TextField[n_g]; g = new TextField[n_g]; b = new TextField[n_g]; for (int i1 = 0; i1 < n_g; i1++) { rgb[i1] = new TextField(3); rgb[i1].setFont(f); rgb[i1].setBackground(new Color(cl[i1].getRed(), cl[i1].getGreen(), cl[i1].getBlue())); jp[i1].add(rgb[i1]); lb[i1][0] = new JLabel(" 赤"); lb[i1][0].setFont(f); jp[i1].add(lb[i1][0]); r[i1] = new TextField(3); r[i1].setFont(f); r[i1].setBackground(Color.white); r[i1].setText(Integer.toString(cl[i1].getRed())); r[i1].addTextListener(this); jp[i1].add(r[i1]); lb[i1][1] = new JLabel("緑"); lb[i1][1].setFont(f); jp[i1].add(lb[i1][1]); g[i1] = new TextField(3); g[i1].setFont(f); g[i1].setBackground(Color.white); g[i1].setText(Integer.toString(cl[i1].getGreen())); g[i1].addTextListener(this); jp[i1].add(g[i1]); lb[i1][2] = new JLabel("青"); lb[i1][2].setFont(f); jp[i1].add(lb[i1][2]); b[i1] = new TextField(3); b[i1].setFont(f); b[i1].setBackground(Color.white); b[i1].setText(Integer.toString(cl[i1].getBlue())); b[i1].addTextListener(this); jp[i1].add(b[i1]); } // 線の変更 if (wd > 0) { JLabel lb1 = new JLabel("線の太さ:"); lb1.setFont(f); jp[n_g].add(lb1); tx = new JTextField(2); tx.setFont(f); tx.setBackground(Color.white); tx.setText(Integer.toString((int)line_w)); jp[n_g].add(tx); } } // TextFieldの内容が変更されたときの処理 public void textValueChanged(TextEvent e) { for (int i1 = 0; i1 < n_g; i1++) { if (e.getSource() == r[i1] || e.getSource() == g[i1] || e.getSource() == b[i1]) { String str = r[i1].getText(); int rc = str.length()>0 ? Integer.parseInt(str) : 0; str = g[i1].getText(); int gc = str.length()>0 ? Integer.parseInt(str) : 0; str = b[i1].getText(); int bc = str.length()>0 ? Integer.parseInt(str) : 0; rgb[i1].setBackground(new Color(rc, gc, bc)); } } } // 値の設定 public void actionPerformed(ActionEvent e) { for (int i1 = 0; i1 < n_g; i1++) { String str = r[i1].getText(); int rc = str.length()>0 ? Integer.parseInt(str) : 0; str = g[i1].getText(); int gc = str.length()>0 ? Integer.parseInt(str) : 0; str = b[i1].getText(); int bc = str.length()>0 ? Integer.parseInt(str) : 0; dd.cl[i1] = new Color(rc, gc, bc); } dd.line_w = Integer.parseInt(tx.getText()); dd.repaint(); setVisible(false); } } /****************************/ /* クラスCoef */ /* coded by Y.Suganuma */ /****************************/ class Coef extends JPanel implements DocumentListener { int type, no; BodePlot_s ba; JTextField n_t; JTextField bun_t[]; JLabel lb[]; JPanel pn; Font f = new Font("TimesRoman", Font.BOLD, 20); /****************************/ /* コンストラクタ */ /* type_i : =0 : 分子 */ /* =1 : 分母 */ /* ba_i : BodePlot_s */ /* no_i : 式番号 */ /****************************/ Coef(int type_i, BodePlot_s ba_i, int no_i) { int i1; type = type_i; ba = ba_i; no = no_i; setBackground(Color.white); // 次数 setLayout(new BorderLayout(5, 5)); JPanel pn1 = new JPanel(); pn1.setBackground(Color.white); add(pn1, BorderLayout.NORTH); String str; if (type == 0) str = (no + 1) + ".分子の次数"; else str = (no + 1) + ".分母の次数"; JLabel jisu = new JLabel(str); jisu.setFont(f); pn1.add(jisu); n_t = new JTextField(2); n_t.setFont(f); n_t.getDocument().addDocumentListener(this); pn1.add(n_t); // 係数 pn = new JPanel(); add(pn, BorderLayout.CENTER); } /************************************/ /* パラメータが変更されたときの処理 */ /************************************/ public void changedUpdate(DocumentEvent e) {} public void removeUpdate(DocumentEvent e) {} public void insertUpdate(DocumentEvent e) { int i1, n; ba.ta.setForeground(Color.black); ba.ta.setText(""); try { n = Integer.parseInt(n_t.getText()); if (n < 0) { n_t.setText("0"); n = 0; } if (type == 0) { ba.m[no] = n; ba.si[no] = new double [n+1]; } else { ba.n[no] = n; ba.bo[no] = new double [n+1]; } remove(pn); pn = new JPanel(); pn.setBackground(Color.white); pn.setLayout(new GridLayout(n+1, 1, 5, 0)); add(pn, BorderLayout.CENTER); JPanel pnx[] = new JPanel [n+1]; bun_t = new JTextField [n+1]; lb = new JLabel [n+1]; pnx[0] = new JPanel(); pnx[0].setBackground(Color.white); pn.add(pnx[0]); lb[0] = new JLabel("定数項"); lb[0].setFont(f); pnx[0].add(lb[0]); bun_t[0] = new JTextField(3); bun_t[0].setFont(f); pnx[0].add(bun_t[0]); for (i1 = 1; i1 <= n; i1++) { pnx[i1] = new JPanel(); pnx[i1].setBackground(Color.white); pn.add(pnx[i1]); lb[i1] = new JLabel("s の "+i1+" 次の項"); lb[i1].setFont(f); pnx[i1].add(lb[i1]); bun_t[i1] = new JTextField(3); bun_t[i1].setFont(f); pnx[i1].add(bun_t[i1]); } getParent().validate(); } catch (NumberFormatException em) {} } } /****************************/ /* クラスComplex */ /* coded by Y.Suganuma */ /****************************/ class Complex { private double r; // 実部 private double i; // 虚部 /******************/ /* コンストラクタ */ /* a : 実部 */ /* b : 虚部 */ /******************/ Complex(double a, double b) { r = a; i = b; } /******************/ /* コンストラクタ */ /* a : 実部 */ /******************/ Complex(double a) { r = a; i = 0.0; } /******************/ /* コンストラクタ */ /******************/ Complex() { r = 0.0; i = 0.0; } /********/ /* 出力 */ /********/ void output(PrintStream out, Complex a) { out.print("(" + a.r + ", " + a.i + ")"); } /******************/ /* 複素数の絶対値 */ /******************/ static double abs(Complex a) { double x; x = Math.sqrt(a.r * a.r + a.i * a.i); return x; } /****************/ /* 複素数の角度 */ /****************/ static double angle(Complex a) { double x; if (a.r == 0.0 && a.i == 0.0) x = 0.0; else x = Math.atan2(a.i, a.r); return x; } /****************/ /* 複素数のn乗 */ /****************/ static Complex pow(Complex a, int n) { int i1; Complex c = new Complex (1); if (n >= 0) { for (i1 = 0; i1 < n; i1++) c = Complex.mul(c, a); } else { for (i1 = 0; i1 < -n; i1++) c = Complex.mul(c, a); c = Complex.dev(new Complex(1.0), c); } return c; } /****************/ /* 複素数の加算 */ /****************/ static Complex add(Complex a, Complex b) { Complex c = new Complex(); c.r = a.r + b.r; c.i = a.i + b.i; return c; } /****************/ /* 複素数の減算 */ /****************/ static Complex sub(Complex a, Complex b) { Complex c = new Complex(); c.r = a.r - b.r; c.i = a.i - b.i; return c; } /****************/ /* 複素数の乗算 */ /****************/ static Complex mul(Complex a, Complex b) { Complex c = new Complex(); c.r = a.r * b.r - a.i * b.i; c.i = a.i * b.r + a.r * b.i; return c; } /****************/ /* 複素数の除算 */ /****************/ static Complex dev(Complex a, Complex b) { double x; Complex c = new Complex(); x = 1.0 / (b.r * b.r + b.i * b.i); c.r = (a.r * b.r + a.i * b.i) * x; c.i = (a.i * b.r - a.r * b.i) * x; return c; } }