/* 07/07/98
 * modifié le 07/03/09
 * modifié le 18/12/17 ajout de main()
 * modification 03/02/21
 */

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;

public class botafumeiro extends JPanel implements Runnable, MouseListener, MouseMotionListener, ActionListener {
  static final long serialVersionUID = 210203L;
  int XMAX, YMAX, X0, Y0, ECH, R;
  int Y1, X2, Y2, X2p, Y2p, XM, YM, XMp, YMp;
  double as, ap, a, amax, sina, cosa, alpha, pi2;
  double amort, g, l, lpr, l0, lp, lr, dt, m, r, rplr, v, vts;
  Thread trace;
  boolean debut, deplace, moteur;
  String label;
  TextField tfdt, tfamort, tm, tvts, tr;
  Button ok;

  private double sd (String s, double d) {
	try {
	  d = Double.parseDouble (s);
	}
    catch (NumberFormatException nfe) {}
    return d;
  }

  private int si (String s, int i) {
	try {
	  i = Integer.parseInt (s);
	}
    catch (NumberFormatException nfe) {}
    return i;
  }

//----initialisation
//----fixe les dimensions de l'écran et le centre du repère
//----et met en place les trois champs de paramètres

  public void init() {
	setBackground (Color.white);
	Font f = new Font ("Arial", Font.PLAIN, 10);
  	setFont (f);
    addMouseMotionListener (this);
    addMouseListener (this);
    add (new Label ("amort. = "));
    tfamort = new TextField ("0.2", 10);
	add (tfamort);
	amort = Math.abs (sd (tfamort.getText(), 0.2));
    add (new Label ("dt = "));
    tfdt = new TextField ("0.014", 10);
	add (tfdt);
	dt = Math.abs (sd (tfdt.getText(), 0.014));
    add (new Label ("v = "));
    tvts = new TextField ("1.0", 10);
	add (tvts);
	vts = Math.abs (sd (tvts.getText(), 1.0));
    add (new Label ("r = "));
    tr = new TextField ("0.05", 10);
	add (tr);
	r = Math.abs (sd (tr.getText(), 0.05));
    ok = new Button ("Ok");
    ok.addActionListener (this);
    add (ok);

//----fixe les valeurs initiales
//----limite les deplacements du pendule à la souris à 10° environ (0,17 rd)

    pi2 = 2.0 * Math.PI;
	ECH = 200;
    v = vts * pi2;
    g = 9.81;
	alpha = 0.0; 
    a = ap = 0.0;
	amax = 0.17;
	sina = 0.0;
	cosa = 1.0;
    lr = 0.2;
	R = (int)(r * ECH);
	rplr = r + lr;
    l0 = 1.0;
	lpr = l = l = l0 - Math.sqrt ( r * r + rplr * rplr);
    debut = true;
	deplace = moteur = false;
    trace = new Thread (this);
    trace.start();
  }

  public void run() {
    while (true) {
	  if (!deplace) {
	    if (moteur) {

//----fait tourner le moteur 

          alpha = alpha + v * dt;
          if (alpha > pi2)
		    alpha = alpha - pi2;
          lpr = l;
          l = l0 - Math.sqrt ( r * r + rplr * rplr + 2 * r * rplr * Math.sin (alpha));
          lp = (l - lpr) / dt;
		}
        else
		  lp = 0.0;
	    sina = Math.sin (a);
		cosa = Math.cos (a);

//----équation différentielle
   
		as = -(g * sina + 2 * lp * ap) / l - amort * ap;

//---calcule la nouvelle vitesse angulaire(ap) et le nouvel angle (a)

		ap = ap + as * dt;
		a = a + ap * dt; }
        repaint ();

//----pose de 10ms = 0,01s

        try {
		  Thread.sleep (10);
	    }
        catch (InterruptedException e) {}
	  }
    }

//----efface et redessine

  public void paint (Graphics g) {
	if (debut || XMAX == 0 || YMAX == 0) {
	  debut = false;
      YMAX = getSize().height;
	  XMAX = getSize().width;
	  g.setColor (Color.white);
      g.fillRect (0, 0, XMAX, YMAX);
      X0 = XMAX / 2; Y0 = 100;
      Y1 = (int)(lr * ECH) + Y0;
	}
    g.setColor (Color.white);
    g.drawLine (X0, Y1, X2p, Y2p);
    g.drawLine (XMp, YMp, X0, Y1);
    g.drawOval (X2p - 20, Y2p - 20, 40, 40);
    g.drawOval (X0 - R, Y0 - R, 2 * R, 2 * R);
    g.drawLine (X0, Y0, XMp, YMp);

    X2p = X2; X2 = (int)(l * sina * ECH) + X0;
    Y2p = Y2; Y2 = (int)(l * cosa * ECH) + Y1;
    XMp = XM; XM = X0 + (int)(Math.cos (alpha) * r * ECH);
    YMp = YM; YM = Y0 - (int)(Math.sin (alpha) * r * ECH);

    g.setColor (Color.green);
    g.drawLine (X0, Y1, X2, Y2);
    g.drawLine (XM, YM, X0, Y1);
    g.setColor (Color.blue);
    g.fillRect (X0 - 3, Y1 - 3, 7, 7);
    g.drawOval (X2 - 20, Y2 - 20, 40, 40);
    g.setColor (Color.red);
    g.drawOval (X0 - R, Y0 - R, 2 * R, 2 * R);
    g.setColor (Color.black);
    g.drawLine (X0, Y0, XM, YM);
    g.fillRect (X2 - 1, Y2 - 1, 3, 3);
  }

//----gestion des évenements liés à la souris et mise à jour des paramètres

    public void mousePressed (MouseEvent e)
    { e.consume ();
      int X = e.getX ();
    	int Y = e.getY ();
    	deplace = (X >=  X2 - 20) & (X <= X2 + 20) & (Y >= Y2 - 20) & (Y <= Y2 + 20);
      boolean b = (X >=  X0 - R) & (X <= X0 + R) & (Y >= Y0 - R) & (Y <= Y0 + R);
      if (b) moteur = ! moteur;
    }

    public void mouseDragged (MouseEvent e)
    { e.consume ();
      int X = e.getX ();
    	int Y = e.getY ();
      if (deplace) { if (Y != Y0) { a = X - X0; a = a / (Y - Y0); a = Math.atan (a); }
                            else { a = Math.PI / 2; if (X < X0) a = - a; }
                            if (Y < Y0) a = a + Math.PI;
                            if (a > amax) a = amax;
                            if (a < - amax) a = - amax;
                            ap = 0.0;
                            sina = Math.sin (a); cosa = Math.cos (a); }
    }

    public void mouseReleased (MouseEvent e)
    { e.consume ();
      deplace = false;
      amort = Math.abs (sd (tfamort.getText(), amort));
      dt = Math.abs (sd (tfdt.getText(), dt));
      vts = Math.abs (sd (tvts.getText(), vts));
      r = Math.abs (sd (tr.getText(), r));
      if (r > 0.2) r = 0.2;
      v = vts * pi2;
    }

  public void mouseMoved (MouseEvent e) {}
  public void mouseClicked (MouseEvent e) {}
  public void mouseEntered (MouseEvent e) {}
  public void mouseExited (MouseEvent e) {}

  public void actionPerformed (ActionEvent e) {
	if (e.getSource () == ok) {
	  amort = Math.abs (sd (tfamort.getText(), amort));
      dt = Math.abs (sd (tfdt.getText(), dt));
      vts = Math.abs (sd (tvts.getText(), vts));
      r = Math.abs (sd (tr.getText(), r));
      if (r > 0.2) r = 0.2;
      v = vts * pi2;
    }
  }

////////////////////////////////////////////////////////////////////////////////

  public static void main (String [] args) {
	int w = 600;
	int h = 400;

	botafumeiro b = new botafumeiro();
	b.XMAX = w;
	b.YMAX = h;
	b.init();

	JFrame f = new JFrame ("Mouvement du pendule");
	f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
	f.add (b);
	f.setSize (w, h);
	f.setVisible (true);

  }

}
