/******************************/ /* 振り子の運動 */ /* coded by Y.Suganuma */ /******************************/ import java.io.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class Test { public static void main(String args[]) throws IOException { Data dt = new Data(); } } /********************/ /* クラスDataの定義 */ /********************/ class Data extends JFrame implements ItemListener { double k; // 摩擦係数 double k1; // フィードバックゲイン double l; // 棒の長さ(m) double m; // 棒の質量(kg) double x0; // 初期角度(deg) double dx0; // 初期角速度(deg/sec) int sw = -1; // =-1 : 振り子 // =1 : 倒立振り子 int err = 0; // エラー番号 JTextField length, weight, friction, feed, ang, ang_v; JComboBoxchoice; Err_p ep; /******************/ /* コンストラクタ */ /******************/ Data() { // Frameクラスのコンストラクタの呼び出し super("データの入力"); // レイアウトの変更 Container cp = getContentPane(); cp.setBackground(Color.white); cp.setLayout(null); JPanel jp = new JPanel(); jp.setLayout(new BorderLayout(5, 10)); jp.setBackground(Color.white); cp.add(jp); JPanel np = new JPanel(); np.setLayout(new GridLayout(8, 3, 5, 10)); np.setBackground(Color.white); jp.add(np, BorderLayout.CENTER); ep = new Err_p(this); ep.setBackground(Color.white); cp.add(ep); Font f = new Font("TimesRoman", Font.BOLD, 20); // スクロールリスト(振り子の種類) JLabel fill1, fill2; fill1 = new JLabel(); np.add(fill1); choice = new JComboBox (); choice.setBackground(Color.white); choice.setFont(f); choice.addItem("振り子"); choice.addItem("倒立振り子"); np.add(choice); choice.addItemListener(this); fill2 = new JLabel(); np.add(fill2); // テキストフィールドの設定 JLabel length_t, length_u, weight_t, weight_u, friction_t, friction_u, feed_t, feed_u, ang_t, ang_u, ang_v_t, ang_v_u, fill3, fill4; // 長さの入力 length_t = new JLabel("長さ"); length_t.setFont(f); np.add(length_t); length = new JTextField(10); length.setFont(f); length.setHorizontalAlignment(JTextField.RIGHT); np.add(length); length_u = new JLabel("meter(>0)"); length_u.setFont(f); np.add(length_u); // 質量の入力 weight_t = new JLabel("質量"); weight_t.setFont(f); np.add(weight_t); weight = new JTextField(10); weight.setFont(f); weight.setHorizontalAlignment(JTextField.RIGHT); np.add(weight); weight_u = new JLabel("kg(>0)"); weight_u.setFont(f); np.add(weight_u); // 摩擦係数の入力 friction_t = new JLabel("摩擦係数"); friction_t.setFont(f); np.add(friction_t); friction = new JTextField("0.0", 10); friction.setFont(f); friction.setHorizontalAlignment(JTextField.RIGHT); np.add(friction); friction_u = new JLabel("(>=0)"); friction_u.setFont(f); np.add(friction_u); // フィードバックゲインの入力 feed_t = new JLabel("フィードバック"); feed_t.setFont(f); np.add(feed_t); feed = new JTextField("0.0", 10); feed.setFont(f); feed.setHorizontalAlignment(JTextField.RIGHT); np.add(feed); feed_u = new JLabel("(>=0)"); feed_u.setFont(f); np.add(feed_u); // 初期角度の入力 ang_t = new JLabel("初期角度"); ang_t.setFont(f); np.add(ang_t); ang = new JTextField(10); ang.setFont(f); ang.setHorizontalAlignment(JTextField.RIGHT); np.add(ang); ang_u = new JLabel("degree"); ang_u.setFont(f); np.add(ang_u); // 初期角速度の入力 ang_v_t = new JLabel("初期角速度"); ang_v_t.setFont(f); np.add(ang_v_t); ang_v = new JTextField("0.0", 10); ang_v.setFont(f); ang_v.setHorizontalAlignment(JTextField.RIGHT); np.add(ang_v); ang_v_u = new JLabel("degree/sec"); ang_v_u.setFont(f); np.add(ang_v_u); // OK ボタンの設定 fill3 = new JLabel(); np.add(fill3); JButton bt = new JButton("OK"); bt.setFont(f); bt.addMouseListener(new ClickMouse()); np.add(bt); fill4 = new JLabel(); np.add(fill4); // Windowサイズを設定 setSize(500, 400); // ウィンドウを表示 setVisible(true); Insets in = getInsets(); jp.setLocation(10, 10); jp.setSize(500-in.left-in.right-20, 350-in.top-in.bottom-20); ep.setLocation(10, 350-in.top-in.bottom); ep.setSize(500-in.left-in.right-20, 50); // イベントアダプタ addWindowListener(new WinEnd()); } /******************************/ /* チョイスコントロールの処理 */ /******************************/ public void itemStateChanged(ItemEvent e) { if (e.getItemSelectable() == choice) { sw = choice.getSelectedIndex(); sw = (sw <= 0) ? -1 : 1; } } /********************************/ /* OKボタンが押されたときの処理 */ /********************************/ class ClickMouse extends MouseAdapter { /************************************/ /* マウスがクリックされたときの処理 */ /************************************/ public void mouseClicked(MouseEvent e) { err = 0; if ((length.getText()).length() <= 0) err = 1; else { l = Double.parseDouble(length.getText()); if (l <= 0.0) err = 1; } if (err > 0) ep.repaint(); else { if ((weight.getText()).length() <= 0) err = 2; else { m = Double.parseDouble(weight.getText()); if (m <= 0.0) err = 2; } if (err > 0) repaint(); else { k = Double.parseDouble(friction.getText()); k1 = Double.parseDouble(feed.getText()); if (k < 0.0 || k1 < 0.0) { err = 3; repaint(); } else { if ((ang.getText()).length() <= 0) x0 = 0.0; else x0 = Double.parseDouble(ang.getText()); dx0 = Double.parseDouble(ang_v.getText()); Link link = new Link(sw, k, k1, l, m, x0, dx0); } } } } } /************/ /* 終了処理 */ /************/ class WinEnd extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } } class Err_p extends JPanel { Data dt; Err_p(Data dt_i) { dt = dt_i; } /**************************/ /* エラーメッセージの表示 */ /**************************/ public void paintComponent (Graphics g) { super.paintComponent(g); // 親クラスの描画(必ず必要) // フォントの設定 Font f = new Font("TimesRoman", Font.BOLD, 20); g.setFont(f); // エラーメッセージの出力 switch (dt.err) { case 0: g.drawString(" ", 10, 30); break; case 1: g.setColor(Color.red); g.drawString("***Error length <= 0***", 10, 30); g.setColor(Color.black); break; case 2: g.setColor(Color.red); g.drawString("***Error weight <= 0***", 10, 30); g.setColor(Color.black); break; case 3: g.setColor(Color.red); g.drawString("***Error k < 0 or k1 < 0***", 10, 30); g.setColor(Color.black); break; default: g.drawString(" ", 10, 30); break; } } } /**********************/ /* クラス Link の定義 */ /**********************/ class Link { private double a[][] = new double [2][2]; // 微分方程式の係数 private double b[] = new double [2]; // 微分方程式の右辺の係数 double h; // 微分方程式を解く際の刻み幅 double x[] = new double [2]; // 角度と角速度 double l; // 棒の長さ(m) int per; // 表示間隔 int slp; // sleep時間(ms) int sw; // =-1 : 振り子 // =1 : 倒立振り子 /**********************************/ /* コンストラクタ */ /* sw_i : =-1 : 振り子 */ /* =1 : 倒立振り子 */ /* k : 摩擦係数 */ /* k1 : フィードバックゲイン */ /* l_i : 棒の長さ(m) */ /* m : 棒の質量(kg) */ /* x0 : 初期角度(deg) */ /* dx0 : 初期角速度(deg/sec) */ /**********************************/ Link(int sw_i, double k, double k1, double l_i, double m, double x0, double dx0) { double a1, b1, c1, D, i, i_mrr, r, mrg, ut, p1, p2; String name; /* 微分方程式の決定 */ l = l_i; sw = sw_i; r = 0.5 * l; i = l * l / 12.0; mrg = m * r * 9.8; i_mrr = i + m * r * r; ut = Math.PI / 180.0; slp = 200; a[0][0] = 0.0; a[0][1] = 1.0; a[1][0] = sw * mrg / i_mrr; a[1][1] = -k / i_mrr; b[0] = -k1 / i_mrr; b[1] = 0.0; /* 初期設定 */ x[0] = x0 * ut; x[1] = dx0 * ut; /* 刻み幅の決定 */ a1 = 1.0; b1 = -a[0][0] - a[1][1]; c1 = -a[0][1] * (a[1][0] + b[0]); D = b1 * b1 - 4.0 * a1 * c1; // 複素根 if (D < 0.0) { p1 = -b1 / (2.0 * a1); p2 = Math.sqrt(-D) / (2.0 * a1); h = 2.0 * Math.PI / p2 / 100.0; } // 実根 else { D = Math.sqrt(D); p1 = Math.abs((-b1 + D) / (2.0 * a1)); p2 = Math.abs((-b1 - D) / (2.0 * a1)); h = (p1 > p2) ? 1.0 / p1 / 50.0 : 1.0 / p2 / 50.0; } /* 表示間隔の決定 */ per = (int)(slp / 1000.0 / h); /* Windowの生成 */ if (sw < 0) name = "振り子の運動"; else name = "倒立振り子の運動"; Display dis = new Display(name, this); } /**************************/ /* 微分方程式 */ /* time : 時間 */ /* x : 位置と速度 */ /* dx : xの微分 */ /**************************/ void link(double time, double [] x, double [] dx) { dx[0] = a[0][0] * x[0] + a[0][1] * x[1]; dx[1] = a[1][0] * Math.sin(x[0]) + b[0] * x[0] + a[1][1] * x[1]; } /*****************************************/ /* ルンゲ・クッタ・ギル法 */ /* time : 現在の時間 */ /* h : 時間刻み幅 */ /* x : 現在の状態 */ /* dx : 微係数(snxで計算する) */ /* w4 : 作業域 */ /* n : 微分方程式の次数(int) */ /* return : time+h */ /*****************************************/ double rkg(double time, double h, double [] x, double [] dx, double [] w4, int n) { int i1, i2; double t0, x1, x2, w3[] = new double [4]; double w1[] = {0.5, 0.29289322, 1.70710678, 0.16666667}; double w2[] = {2.0, 1.0, 1.0, 2.0}; w3[0] = .0; w3[1] = 0.5 * h; w3[2] = w3[1]; w3[3] = h; t0 = time; for (i1 = 0; i1 < n; i1++) w4[i1] = 0.0; for (i1 = 0; i1 < 4; i1++) { time = t0 + w3[i1]; link(time, x, dx); for (i2 = 0; i2 < n; i2++) { x1 = h * dx[i2]; x2 = w1[i1] * (x1 - w2[i1] * w4[i2]); x[i2] += x2; if (i1 != 3) w4[i2] += 3 * x2 - w1[i1] * x1; } } return(time); } } /***********************/ /* クラスDisplayの定義 */ /***********************/ class Display extends JFrame implements Runnable { private double time; // 時刻 double ritu; // 表示倍率 int size; // Windowの大きさ int h; // 左右上下の余裕 int w; // Windowのタイトル幅 private boolean win_state = true; // Windowの状態 private Link link; // Linkクラスのオブジェクト private Thread th; // Threadクラスのオブジェクト Display ds = this; Draw_p dp; /******************************************/ /* コンストラクタ */ /* name : タイトル */ /* link_i : Linkクラスのオブジェクト */ /******************************************/ Display(String name, Link link_i) { // Frameクラスのコンストラクタの呼び出し super(name); // データの設定 link = link_i; size = 500; h = 50; w = 10; ritu = (0.5 * size - 10) / link.l; time = 0.0; Container cp = getContentPane(); cp.setBackground(Color.white); cp.setLayout(null); dp = new Draw_p(link, this); dp.setBackground(Color.white); cp.add(dp); // Windowの大きさの設定 setSize(size+2*h+2*w, size+2*h+2*w); // ウィンドウを表示 setVisible(true); dp.setLocation(h, h); dp.setSize(500, 500); // イベントアダプタ addWindowListener(new WinEnd()); // スレッドの定義と開始 th = new Thread(this); th.start(); } /******************/ /* スレッドの実行 */ /******************/ public void run() { double dx[] = new double [2]; double wk[] = new double [2]; int i1; repaint(); while (win_state) { for (i1 = 0; i1 < link.per; i1++) time = link.rkg(time, link.h, link.x, dx, wk, 2); try { th.sleep(link.slp); } catch (InterruptedException e) {} dp.repaint(); } } /************/ /* 終了処理 */ /************/ class WinEnd extends WindowAdapter { public void windowClosing(WindowEvent e) { win_state = false; ds.setVisible(false); } } } class Draw_p extends JPanel { private Link link; private Display ds; Draw_p(Link link_i, Display ds_i) { link = link_i; ds = ds_i; } /********/ /* 描画 */ /********/ public void paintComponent (Graphics g) { super.paintComponent(g); // 親クラスの描画(必ず必要) int b_x = ds.size / 2, b_y = ds.size / 2, x, y; // 中心 g.setColor(Color.black); g.fillOval(b_x-5, b_y-5, 10, 10); // 棒 g.setColor(Color.blue); if (link.sw < 0) { x = b_x + (int)(link.l * Math.sin(link.x[0]) * ds.ritu + 0.5); y = b_y + (int)(link.l * Math.cos(link.x[0]) * ds.ritu + 0.5); } else { x = b_x + (int)(link.l * Math.sin(link.x[0]) * ds.ritu + 0.5); y = b_y - (int)(link.l * Math.cos(link.x[0]) * ds.ritu + 0.5); } g.drawLine(b_x, b_y, x, y); } }