/**
 * trace une cardioïde
 * 26/09/98 - double-buffering et adapté jdk 1.2 le 02/04/99
 *
 * @ author Jean-Paul Quelen
 * ajout de main() le 19/12/17 modifié le 15/09/22
 */

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;

public class cardio2 extends Panel implements Runnable, ActionListener {
	static final long serialVersionUID = 220915L;
	int xymax, xy0, r, rx2, rx4, xy0m2r, xy0m3r, DT, xP, yP, xMp, yMp, xy0mr;
	int xy0pr, xM, yM, xM1, yM1, xP1, yP1, xMp1, yMp1, xC, yC, xC1, yC1;
	double t, dt, adt;
	static Thread trace;
	boolean btrace = true;
	TextField TFadt, TFDT;
	Button recule, avance, stop, tr;
	static Image img;
	static Graphics g;
	int gw, gh;
	int epPoint = 5;
 
	public cardio2() {
		setFont (new Font (Font.SANS_SERIF, Font.PLAIN, 10));
		setBackground (Color.WHITE);
		t = 0.0;
		dt = adt = 0.1;
		DT = 100;
		add (recule = new Button ("<<"));
		recule.addActionListener (this);
		add (avance = new Button (">>"));
		avance.addActionListener (this);
		add (stop = new Button ("Stop"));
		stop.addActionListener (this);
		add (tr = new Button ("Trace"));
		tr.addActionListener (this);
		add (new Label ("dt ="));
		add (TFadt = new TextField ("0.1", 10));
		add (TFDT = new TextField ("100", 10));
		add (new Label ("ms"));
		trace = new Thread (this);
		trace.start();
	}

	public void run() {
		Thread th = Thread.currentThread();
		while (th == trace) {
			repaint();
			try {
				Thread.sleep (DT);
			}
			catch (InterruptedException e) {}
		}
	}

	public void update (Graphics g1) {
		paint (g1);
	}

	public void paint (Graphics g1) {
		if (img == null || gw != getSize().width || gh != getSize().height) {
			gw = getSize().width;
			gh = getSize().height;
			img = createImage (gw, gh);
			g = img.getGraphics();
			xymax = getSize().height;
			xy0 = getSize().width;
			xymax = (xymax > xy0 ? xy0 : xymax);
			xy0 = xymax / 2;
			r = xymax / 8;
			rx2 = xymax / 4;
			rx4 = xymax / 2;
			xy0mr = xy0 - r;
			xy0pr = xy0 + r;
			xy0m2r = xy0 - rx2;
			xy0m3r = xy0 - rx2 - r;
			xP = xy0mr; yP = xy0;
			xC = yC = xy0 + rx2;
			xM = xy0pr;
			yM = xy0;
			xMp = xy0m3r;
			yMp = xy0;
			g.setColor (Color.WHITE);
			g.fillRect (0, 0, gw, gh);
		}
		g.setColor (Color.WHITE);
		g.drawOval (xy0mr + xC1, xy0mr + yC1, rx2, rx2);
		g.drawLine (xM1, yM1, xMp1, yMp1);
		g.drawOval (xP1 - rx2, yP1 - rx2, rx4, rx4);
		g.fillRect (xP1 - 2, yP1 - 2, epPoint, epPoint);
		if (!btrace) {
			g.fillRect (xM1 - 2, yM1 - 2, epPoint, epPoint);
			g.fillRect (xMp1 - 2, yMp1 - 2, epPoint, epPoint);
		}

		g.setColor (Color.RED);
		g.drawLine (0, xy0, xymax, xy0);
		g.drawLine (xy0, 0, xy0, xymax);

		g.setColor (Color.YELLOW);
		g.drawOval (xy0mr + xC, xy0mr + yC, rx2, rx2);

		g.setColor (Color.BLACK);
		g.drawOval (xy0mr, xy0mr, rx2, rx2);
		g.drawLine (xM, yM, xMp, yMp);
		g.drawOval (xP - rx2, yP - rx2, rx4, rx4);

		g.setColor (Color.BLUE);
		g.fillRect (xP - 2, yP - 2, epPoint, epPoint);
		g.fillRect (xy0pr - 2, xy0 - 2, epPoint, epPoint);
		g.fillRect (xM - 2, yM - 2, epPoint, epPoint);
		g.fillRect (xMp - 2, yMp - 2, epPoint, epPoint);

		g1.drawImage (img, 0, 0, this);

		t -= dt;
		xP1 = xP;
		yP1 = yP;
		xM1 = xM;
		yM1 = yM;
		xC1 = xC;
		yC1 = yC;
		xMp1 = xMp;
		yMp1 = yMp;
		xC = (int) (Math.cos (t) * (double) rx2); 
		yC = (int) (Math.sin (t) * (double) rx2); 
		xM = (int) (r * (2.0 * Math.cos (t) - Math.cos (2.0 * t))) + xy0;
		yM = (int) (r * (2.0 * Math.sin (t) - Math.sin (2.0 * t))) + xy0;
		xMp = (int) (r * (-2.0 * Math.cos (t) - Math.cos (2.0 * t))) + xy0;
		yMp = (int) (r * (-2.0 * Math.sin (t) - Math.sin (2.0 * t))) + xy0;
		xP = (xM + xMp) / 2;
		yP = (yM + yMp) / 2;
	}

	public void actionPerformed (ActionEvent e) {
		if (e.getSource() == recule)
			dt = adt;
		else if (e.getSource() == avance)
			dt = - adt;
		else if (e.getSource() == stop)
			dt = 0;
		else if (e.getSource() == tr)
			btrace = !btrace;
		adt = Math.abs (Double.parseDouble(TFadt.getText()));
		DT = Math.abs (Integer.parseInt (TFDT.getText()));
	}

	public static void main (String [] args) {
		cardio2 c = new cardio2();
		JFrame f = new JFrame ("cardio2");
		f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		f.add (c);
		f.setSize (500, 500);
		f.setVisible (true);
	}

}
