/******************************/
/* 振り子の運動 */
/* 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;
JComboBox choice;
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);
}
}