/**
 * henon.java 18/04/20
 *
 * trace l'attracteur de Hénon avec des couleurs aléatoires
 * possibilité de zoomer et de changer de paramètres
 *
 * @ author Jean-Paul Quelen
 */

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class henon extends JPanel implements MouseListener, ActionListener {
	static final long serialVersionUID = 200418L;
	int xmax, ymax, Niter, NCouleur, x, y, n, nm1;
	double Zoom, Rayon2, pas, xo, yo, aH, bH;
	double Cy, Cx, Zx, Zy, Z;
	int [] [] TCouleur;
	Random r;
	TextField tfx, tfy, mult;
	Button b;
	
	private double tfd (TextField tf, double d) {
		try {
			d = Double.parseDouble (tf.getText());
		}
		catch (NumberFormatException nfe) {}
		tf.setText (Double.toString (d));
		return d;
	}

	public henon() {
		setCursor (new Cursor (Cursor.CROSSHAIR_CURSOR));
		Font f = new Font (Font.SANS_SERIF, Font.PLAIN, 10);

		add (mult = new TextField ("x 1", 10));
		mult.setFont (f);
		mult.setBackground (Color.black);
		mult.setForeground (Color.white);

		add (b = new Button ("Nouveau tracé"));
		b.addActionListener (this);
		b.setFont (f);

		aH = 1.4;
		add (tfx = new TextField ("1.4", 10));

		bH = 0.3;
		add (tfy = new TextField ("0.3", 10));
		tfy.setFont (f);

		NCouleur = 256;
		Niter = 256;
		addMouseListener (this);
		init();
	}

	private void init() {
		xo = yo = 0.0;
		Zoom = 1.0;
		Rayon2 = 4.0;
		pas = 4.0 / Zoom / (xmax > ymax ? xmax : ymax);
		Cy = yo - ymax * pas / 2.0;
		y = 0;
		mult.setText ("x 1");
		TCouleur = new int [Niter] [3];
		r = new Random ();
		int Niterm1 = Niter - 1;
		TCouleur [Niterm1] [0] = TCouleur [Niterm1] [1] = TCouleur [Niterm1] [2] = 0;
		for (n = 0; n < Niterm1; n++)
			for (int p = 0; p < 3; p++)
				TCouleur [n] [p] = Math.abs (r.nextInt ()) % NCouleur;
		tfx.setText(Double.toString(aH));
		tfy.setText(Double.toString(bH));
	}
	 
	public void update (Graphics g) {
		paint (g);
	}

	public void paint (Graphics g) {
		if (xmax != getWidth() || ymax != getHeight()) {
			xmax = getWidth();
			ymax = getHeight();
			init();
		}
		while (y < ymax) {
			Cx = xo - xmax * pas / 2.0;
			for (x = 0; x < xmax; x++) {
				Zx = Cx;
				Zy = Cy;
				n = 1;
				while (((Zx * Zx + Zy * Zy) < Rayon2) & (n < Niter)) {
					Z = Zy + 1.0 - aH * Zx * Zx;
					Zy = bH * Zx;
					Zx = Z;
					n++;
				}
				nm1 = n - 1;
				g.setColor (new Color (TCouleur [nm1] [0], TCouleur [nm1] [1], TCouleur [nm1] [2])); 
				g.drawLine (x, y, x, y);
				Cx += pas;
			}
			Cy += pas;
			y++;
		}
	}

	public void actionPerformed (ActionEvent e) {
		if (e.getSource() == b) {
			aH = tfd (tfx, aH);
			bH = tfd (tfy, bH);
			init();
			repaint();
		}
	}

	public void mousePressed (MouseEvent e) {
		aH = tfd (tfx, aH);
		bH = tfd (tfy, bH);
		xo += pas * (e.getX() - xmax / 2.0);
		yo += pas * (e.getY() - ymax / 2.0);
		Zoom *= 2.0;
		pas = 4.0 / Zoom / (xmax > ymax ? xmax : ymax);
		Cy = yo - ymax * pas / 2.0;
		y = 0;
		mult.setText ("x " + (int) Zoom);
		repaint();
	}

	public void mouseReleased (MouseEvent e) {}
	public void mouseClicked (MouseEvent e) {}
	public void mouseEntered (MouseEvent e) {}
	public void mouseExited (MouseEvent e) {}

////////////////////////////////////////////////////////////////////////////////

	public static void main (String [] args) {
		int w = 600;
		int h = 600;

		henon hen = new henon();

		JFrame f = new JFrame ("Attracteur de Hénon");
		f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		f.add (hen);
		f.setSize (w, h);
		f.setVisible (true);
	}

}
