// 振り子運動をするボール
// <applet code="pendulum.class" width="220" height="510"></applet>
import java.applet.* ;
import java.awt.* ;
import java.awt.event.* ;

public class pendulum extends Applet
                      implements Runnable, ActionListener {

  // 共通変数を定義
  Thread   th = null ;                          // スレッド
  Button   bt_start_stop = new Button("開始") ; // 開始/停止ボタン
  TextField tf1 = new TextField(15), tf2 = new TextField(15) ;
  double   a, v ;                               // 現在の角度,角速度
  boolean  isRunning = false ;                  // 動作中かどうかを示すフラグ
  Image    backIMG = null ;                     // 裏画面
  Graphics backG = null ;
  Image    graphIMG = null ;                    // グラフ描画
  Graphics graphG = null ;
  int      gx=0, gy=0 ;
  final private int szApp = 200 ;
  final private int szImg = szApp*8/10 ;
  final private int szRad = szApp*3/10 ;
  // 振り始めの角度
  final private double a0  = Math.PI/3.0 ;
  // 振り始めの速度
  final private double v0  = 0.0 ;
  final private double h   = 0.01 ;
  final private int offsL = szApp/10 ;

  // GUI 部品の配置と初期値の設定
  public void init() {
    add(tf1) ; add(new Label("rad.")) ;
    add(tf2) ; add(new Label("rad./sec.")) ;
    add(bt_start_stop) ;
    tf1.setEditable(false) ;
    tf2.setEditable(false) ;
    bt_start_stop.addActionListener( this ) ;
  }

  private void nextPos() {
    double y1, y2, df1, df2 ;
    y1 = h*v ;
    y2 = h*(-980.0/(double)szRad*Math.sin(a)) ;
    df1 = h*(v+y2*0.5) ;
    df2 = h*(-980.0/(double)szRad*Math.sin(a+y1*0.5)) ;
    a += df1 ;
    v += df2 ;
  }

  // 中心からの角度 t によるボールの描画
  private void paintBack() {
    if( graphG==null || backG==null ) return ;
    int    x, y ;
    x = (int)((double)szRad*Math.sin(a)) ;
    y = (int)((double)szRad*Math.cos(a)) ;
    gx = (int)(a*50.0)+100 ;
    graphG.drawOval( gx, gy, 1, 1 ) ;
    if( gy == 190 ) {
      graphG.copyArea(0,100,200,100,0,-100) ;
      graphG.setColor(Color.white) ;
      graphG.fillRect(0,100,200,100) ;
      graphG.setColor(Color.black) ;
      graphG.drawLine(100,100,100,200) ;
      gy = 90 ;
      graphG.setColor(Color.green) ;
      graphG.drawOval( gx, gy, 1, 1 ) ;
    }
    gy++ ;
    // 裏画面を白で塗りつぶし
    backG.setColor(Color.white) ;
    backG.fillRect(0,0,szImg,szImg) ;
    backG.setColor(Color.black) ;
    backG.drawLine(szImg/2,szImg/2,szImg/2+x,szImg/2+y) ;
    // 画面内に収めるため,平行移動させてボールを描画
    backG.setColor(Color.blue) ;
    backG.fillOval(szImg/2+x-5,szImg/2+y-5, 11, 11) ;
    tf1.setText(Double.toString(v)) ; tf1.repaint() ;
    tf2.setText(Double.toString(a)) ; tf2.repaint() ;
  }

  public void start() {
    backIMG = createImage(szImg,szImg) ;
    backG = backIMG.getGraphics() ;
    a = a0 ;
    v = v0 ;
    graphIMG = createImage(200,200) ; // グラフを初期化
    graphG = graphIMG.getGraphics() ;
    graphG.setColor(Color.white) ;
    graphG.fillRect(0,0,200,200) ;
    gx = 0 ;
    graphG.setColor(Color.black) ;
    graphG.drawLine(100,0,100,200) ;
    graphG.setColor(Color.green) ;
    paintBack() ;
    th = new Thread(this) ;
    th.start() ;
  }

  public void run() {
    // アニメーション全体のコントロール
    while( th!=null ) {
      if( isRunning ) {
	nextPos() ;   // 位置・速度を更新
	paintBack() ;  // 裏画面にボールを描く
	repaint() ;
      }
      try {
	th.sleep(10) ;     // 0.01秒休止
      }
      catch( InterruptedException e ) {
      }
    }
  }

  public void actionPerformed( ActionEvent ev ) {
    if( ev.getSource() == bt_start_stop ) {
      isRunning = !isRunning ;
      if( !isRunning ) { // 停止中であれば
	bt_start_stop.setLabel("開始") ; bt_start_stop.repaint() ;
      }
      else {
	bt_start_stop.setLabel("停止") ; bt_start_stop.repaint() ;
      }
    }
  }

  public void update(Graphics g) {
    paint(g) ;
  }
  public void paint(Graphics g) {
    if( backIMG != null && graphIMG != null ) {
      // 裏画面に描かれた絵を表示画面に転送
      g.drawImage(backIMG,10+offsL,100,this) ;
      g.drawImage(graphIMG,10,szApp+100,this) ;
    }
  }

}