import java.applet.Applet; // アプレットを使うため
import java.awt.*;         // Graphics と Dimension を使うため
import java.awt.event.*; // イベントを取り扱うために必要
/* 
   <APPLET CODE="HakeiApplet" WIDTH=600 HEIGHT=400>
   </APPLET>
*/

/**
  波形を描くアプレット HakeiApplet
  Copyright 1999 Hidetaka Masuda
 */
public class HakeiApplet extends Applet implements ActionListener,ItemListener {
    TextField maxValueTF; // テキストフィールドを入れる 
  Choice hakeiC;   // チョイスを入れる
  int x0, y0;      // 原点のピクセル値座標
  double kx, ky;   // 各軸の描画の倍率
  Dimension d;     // アプレットの大きさ
  int max=1;            // 最大調波数
  int hakei=0;     // どの波形を描くか
  double alpha=Math.PI / 3; // 台形の角α

  /**
    初期化 init()
   */
  public void init() {
    hakeiC = new Choice();  // チョイスの生成
    hakeiC.add("Kukei");    // 項目の追加
    hakeiC.add("Nokogiri");  // 項目の追加
    hakeiC.add("Sankaku"); // 項目の追加
    hakeiC.add("Zenpa"); // 項目の追加
    hakeiC.add("Daikei"); // 項目の追加
    hakeiC.addItemListener(this); // フレームをリスナとして追加
    add("North", hakeiC);         // チョイスをアプレットに追加
    maxValueTF = new TextField(Integer.toString(max), 10); 
                                        // テキストフィールドの生成
    maxValueTF.addActionListener(this); // フレームをリスナとして追加
    add("South", maxValueTF);       // テキストフィールドをアプレットに追加
  }

  /**
    各係数の設定 setVariables()
   */
  public void setVariables() {
    d = getSize();
    x0 = d.width / 4;
    y0 = d.height / 2;
    kx = d.width / 4 / Math.PI;
    ky = d.height * 0.8 / 2 / 1.0;
  }

  /**
    paint()メソッドの定義
   */
  public void paint(Graphics g) {
    setVariables(); // 現在のアプレットの大きさを得て係数を設定
    drawAxis(g);    // 軸を描く
    drawSineCurve(g); // カーブを描く
  }

  /**
    軸を描く drawAxis()
   */
  public void drawAxis(Graphics g) {
    g.drawLine(0, y0, d.width, y0);  // x軸
    g.drawLine(x0, 0, x0, d.height); // y軸
  }

  /**
    カーブを描く drawSineCurve()
   */
  public void drawSineCurve(Graphics g) {
    int pixelX, pixelY, prevX, prevY; // x、y、直前のx、直前のyのピクセル値
    double x, y; // 二次元座標上のx、y
    prevX = 0;           // 最初のx描画点
    x = toX(prevX);      // x座標値へ
    y = hakei(x);   // 関数の計算
    prevY = toPixelY(y); // 最初のy描画点 
    for (pixelX=0; pixelX<d.width; pixelX=pixelX+1) { // 0から幅まで
      x = toX(pixelX);          // ピクセル値から実際のx値へ 
      y = hakei(x);        // y値を計算
      pixelY = toPixelY(y);     // ピクセル値へ変換
      g.drawLine(pixelX, pixelY, prevX, prevY); 
                                   // 現在の値から直前の値まで直線を引く
      prevX = pixelX;                   // 現在のxピクセル値を直前の値に
      prevY = pixelY;                   // 現在のyピクセル値を直前の値に
    }
  }

  /**
    波形計算呼出しメソッド hakei()
  */
  double hakei(double theta) {
    double answer;
    switch (hakei) {
    case 0: answer = kukeiha(theta); break;
    case 1: answer = nokogiriha(theta); break;
    case 2: answer = sankakuha(theta); break;
    case 3: answer = zenpa(theta); break;
    case 4: answer = daikeiha(theta); break;
    default: answer = sine(theta); break;
    };
    return answer;
  }

  /**
    関数の定義メソッド(矩形波の関数) kukeiha()
  */
  double kukeiha(double theta) {
    double answer=0;
    for(int i=1; i<=max; i=i+1) { // 2*max-1調波までの関数の和
      answer = answer + 1.0/(2*i-1) * Math.sin(theta * (2*i-1));
    }
    return answer;
  }

  /**
    関数の定義メソッド(三角波の関数) sankakuha()
  */
  double sankakuha(double theta) {
    double answer=0;
    int j=-1;
    answer = Math.sin(theta);
    for(int i=1; i<=max; i=i+1) { // max調波までの関数の和 偶数マイナス
      answer = answer + 1.0/Math.pow((2 * i +1), 2) * Math.sin(theta * (2 * i + 1)) * j;
      j = -j;
    }
    return answer * 0.8;
  }

  /**
    関数の定義メソッド(のこぎり波の関数) nokogiriha()
  */
  double nokogiriha(double theta) {
    double answer=0;
    int j=-1;
    for(int i=1; i<=max; i=i+1) { // max調波までの関数の和
      j = -j;
      answer = answer + 1.0/(i) * Math.sin(theta * (i)) * j;
    }
    return answer / 2.0;
  }

  /**
    関数の定義メソッド(全波整流の関数) zenpa()
  */
  double zenpa(double theta) {
    double answer=0;
    answer = 1;
    for(int i=1; i<=max; i=i+1) { // max調波までの関数の差
      answer = answer - 2.0/(2*i-1)/(2*1+1) * Math.cos(theta * (2 * i));
    }
    return answer / 2.0;
  }

  /**
    関数の定義メソッド(台形波の関数) daikeiha()
  */
  double daikeiha(double theta) {
    double answer=0;
    answer = Math.sin(theta) * Math.sin(alpha);
    for(int i=1; i<=max; i=i+1) { // max調波までの関数の和 偶数マイナス
      answer = answer + 1.0/Math.pow((2 * i +1), 2) * Math.sin(theta * (2 * i + 1)) * Math.sin((2 * i + 1));
    }
    answer = answer / alpha;
    return answer;
  }

  /**
    関数の定義メソッド(サイン波の関数) sine()
  */
  double sine(double theta) {
    double answer;
    answer = Math.sin(theta);
    return answer;
  }

  /** 
    xピクセル値への変換メソッド toPixelX()
  */
  int toPixelX(double x) {
    return (int) (x0 + kx * x);
  }

  /** 
    yピクセル値への変換メソッド toPixelY()
  */
  int toPixelY(double y) {
    return (int) (y0 - ky * y);
  }

  /** 
    x座標値への変換メソッド toX()
  */
  double toX(int pixelX) {
    return (pixelX - x0) / kx;
  }

  /** 
    y座標値への変換メソッド toY()
  */
  double toY(int pixelY) {
    return - (pixelY - y0) / ky;
  }
  /**
    テキストフィールドのアクション(Enterキーが押された)処理 actionPerformed()
   */
  public void actionPerformed(ActionEvent ae) { 
                                   // Enterキーが押された時に行う処理
    String inputString = maxValueTF.getText() ; // 入力文字列を得る
    max = Integer.parseInt(inputString);        // 整数に変換
    if (max>=0) { // max が0より大きい時だけ
      repaint();      // グラフを描く
    }
  }
  /**
    チョイスのアクション処理 itemStateChanged()
   */
  public void itemStateChanged(ItemEvent ie) { 
                                   // 項目変更時に行う処理
    hakei = hakeiC.getSelectedIndex();
  }

}
