/**
 * trace une cardioïde
 * 26/09/98 - double-buffering et adapté jdk 1.2 le 02/04/99
 *
 * @ author Jean-Paul Quelen
 * main() ajouté le 18/12/17 et modification le 15/09/22
 */

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;

public class cardio extends Panel implements Runnable, ActionListener {
	static final long serialVersionUID = 220915L;
	int xyMax, xy0, r, rx2, DT;
	int xM, yM, xM1, yM1, xC, yC, xC1, yC1, xy0mr, xy0pr, xy0m2;
	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 cardio() {
		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 = gh;
			xy0 = gw;
			xyMax = (xyMax > xy0 ? xy0 : xyMax);
			xy0 = xyMax / 2;
			r = xyMax / 8;
			rx2 = xyMax / 4;
			xy0mr = xy0 - r;
			xy0pr = xy0 + r;
			xy0m2 = xy0 - 2;
			xC = yC = xy0 + rx2;
			xM = r;
			yM = 0;
			g.setColor (Color.WHITE);
			g.fillRect (0, 0, gw, gh);
	}
	g.setColor (Color.WHITE);
		g.drawOval (xy0mr + xC1, xy0mr + yC1, rx2, rx2);
		if (!btrace)
			g.fillRect (xM1 + xy0 - 2, yM1 + xy0 - 2, epPoint, epPoint);

		g.setColor (Color.RED);
		g.drawLine (0, xy0, xyMax, xy0);
		g.drawLine (xy0, 0, xy0, xyMax);

		g.setColor (Color.BLACK);
		g.drawOval (xy0mr, xy0mr, rx2, rx2);
		g.drawOval (xy0mr + xC, xy0mr + yC, rx2, rx2);

		g.setColor (Color.BLUE);
		g.fillRect (xM + xy0m2, yM + xy0m2, epPoint, epPoint);
		g1.drawImage (img, 0, 0, this);

		xC1 = xC;
		yC1 = yC;
		xM1 = xM;
		yM1 = yM;
		t = t - dt;
		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 * t)));
		yM = (int) (r * (2.0 * Math.sin (t) - Math.sin (2 * t)));
	}

	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) {
		cardio c = new cardio();
		JFrame f = new JFrame ("Cardioïde");
		f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		f.add (c);
		f.setSize (500, 500);
		f.setVisible (true);
	}

}
