/*
Datei............: Aufg2019.java
Projekt..........: Einführung in die Java-Programmierung
Erstellt.........: 10.12.97, Guido Krüger
Geändert.........: --
Aufgabe..........: Musterlösung zu Aufgabe 20.19
Kommentare.......:

Diese Klasse implementiert einen Frame, dessen Bildschirmausgabe in 
einem eigenen Thread erfolgt, der innerhalb von paint erzeugt wird. Die
"Rechenintelligenz" des Programms sitzt demnach in der Methode run()
der Klasse Aufg2019. Hier wird zunächst die Größe des Bildschirms bestimmt
und mit Hilfe der Parameter SETSTARTX, SETSTARTY, SETSIZEX und SETSIZEY,
die die Größe des aktuellen Ausschnitts der Mandelbrotmenge angeben, der
gewählte Ausschnitt in der möglichen Auflösung berechnet. Um das Programm
auch weiterhin zügig auf Useraktionen reagieren zu lassen, wird mit jedem
Aufruf von paint() der aktuelle Thread gekillt und mit den aktuellen
Parametern ein neuer gestartet.

Die Farben jedes einzelnen Bildpunktes werden abhängig von der 
Iterationstiefe aus einem Farbarray entnommen. Dieses wird am Anfang
des Programms mit der Methode Color.getHSBColor so initialisiert, daß
die Übergänge zwischen benachbarten Farben minimal sind und alle
Farben verwendet werden.

Das Skalieren des Ausschnitts kann mit Hilfe der Maus erfolgen. Wir
haben hier tatsächlich ein sehr einfaches Verfahren realisiert, bei
dem (ohne visuelles Feedback) nacheinander zwei Punkte auf dem Bildschirm 
angeklickt werden müssen, die zunächst die linke obere und dann die
rechte untere Ecke des neuen Bildausschnitts angeben. Eine sinnvollere
Benutzerführung wäre hier sicherlich ein DragRechteck, wie es in
Aufgabe 20.11 mit der Klasse MouseDragListener realisiert wurde. Die
Einbindung ist nicht schwierig und kann als Übung erfolgen.
*/
import java.awt.*;
import java.awt.event.*;

class Aufg2019
extends Frame
implements Runnable
{
  static final int MAXIT = 100;
  Thread thread;
  double SETSTARTX = -2.0;
  double SETSTARTY = -1.0;
  double SETSIZEX  =  3.0;
  double SETSIZEY  =  2.0;
  int startx, starty, sizex, sizey;
  DoublePoint firstClick;
  Color arColors[];

  public static void main(String args[])
  {
	Aufg2019 wnd = new Aufg2019();
	wnd.setSize(400,350);
	wnd.setVisible(true);
  }

  public Aufg2019()
  {
	super("Aufg2019");
	thread = null;
	firstClick = null;
	//Farbarray initialisieren
	arColors = new Color[MAXIT];
	for (int i = 0; i < MAXIT; ++i) {
	  arColors[i] = Color.getHSBColor(i/(float)MAXIT,1.0f,1.0f);
	}
    //Listenerklassen registrieren
    addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent event)
        {
          if (thread != null) {
            thread.stop();
			thread = null;
		  }
		  setVisible(false);
		  dispose();
		  System.exit(0);
        }
      }
    );
	addMouseListener(new MyMouseListener());
  }

  public void paint(Graphics g)
  {
	if (thread != null) {
	  thread.stop();
	  thread = null;
	}
	thread = new Thread(this);
	thread.start();
  }

  public void run()
  {
	int cnt;
	double cx, cy, zx, zy, tmpzx;
	Graphics g = getGraphics();
	startx = getInsets().left;
	starty = getInsets().top;
	sizex  = getSize().width - startx - getInsets().right;
	sizey  = getSize().height - starty - getInsets().bottom;

	for (int y = 0; y < sizey; ++y) {
	  for (int x = 0; x < sizex; ++x) {
		cnt = 0;
		cx = SETSTARTX + SETSIZEX * ((double)x / sizex);
		cy = SETSTARTY + SETSIZEY * ((double)y / sizey);
		zx = 0.0;
		zy = 0.0;
		while (true) {
		  ++cnt;
		  tmpzx = zx;
		  zx = (zx * zx) - (zy * zy) + cx;
		  zy = (2 * tmpzx * zy) + cy;
		  if (cnt >= MAXIT || ((zx * zx) + (zy + zy)) > 4) {
			break;
		  }
		}
		if (cnt >= MAXIT) {
		  g.setColor(Color.black);
		} else {
		  g.setColor(arColors[cnt]);
		}
		g.drawLine(x + startx, y + starty, x + startx, y + starty);
	  }
	}
	g.dispose();
  }

  class MyMouseListener
  extends MouseAdapter
  {
	public void mousePressed(MouseEvent event)
	{
	  int xpos = event.getX();
	  int ypos = event.getY();

	  if (xpos > startx && ypos > starty) {
		double x = SETSTARTX + SETSIZEX * ((xpos - startx) / (double)sizex);
		double y = SETSTARTY + SETSIZEY * ((ypos - starty) / (double)sizey);

		if (firstClick == null) { //erster Klick
		  firstClick = new DoublePoint(x, y);
		} else {                  //zweiter Klick
		  SETSTARTX = firstClick.x;
		  SETSTARTY = firstClick.y;
		  SETSIZEX  = x - firstClick.x;
		  SETSIZEY  = y - firstClick.y;
		  firstClick = null;
		  repaint();
		}
	  }
	}
  }

  /**
   * Diese Klasse faßt zwei Fließkommawerte zusammen.
   */
  class DoublePoint
  {
	public double x;
	public double y;

	public DoublePoint(double x, double y)
	{
	  this.x = x;
	  this.y = y;
	}
  }
}

