Das Apfelmännchen/ FraktaleMengeAusgabe

Aus Wikibooks
Zur Navigation springen Zur Suche springen
/*
  Source file: FraktaleMengeAusgabe.java
  Author: Markus Bautsch
  Licence: public domain
  Date: 9. April 2021
  Version: 1.0
  Programming language: Java
 */

/*
  Graphische Ausgabe von Julia- und Mandelbrot-Mengen.
  Mandelbrot-Mengen werden mit dem konstanten komplexwertigen Parameter c = (0, 0) gekennzeichnet.
 */

public final class FraktaleMengeAusgabe extends javax.swing.JFrame
{
	// serialVersionUID fuer die Serialisation, die in der Klasse javax.swing.JFrame implementiert ist.
	private final static long serialVersionUID = 1L;

	// Klassenkonstanten
	final public static boolean DontDisplay = false;
	final public static boolean Display = true;
	final public static boolean DontSave = false;
	final public static boolean Save = true;
	
	// Instanzkonstanten
	final private KomplexeZahl c; 
	final private double minX;
	final private double maxX;
	final private double minY;
	final private double maxY;
	final private int pixelZahlX;
	final private int pixelZahlY;
	final private boolean mandelbrot;

	// Instanzvariablen
	private boolean display;
	private boolean save;

	/* Konstruktor fuer die Initialisierung von Instanzen der Klasse FraktaleMengeAusgabe.
	 * @param c: Komplexwertiger Koeffizient der quadratischen Polynome
	 * @param minX: Kleinster x-Wert der rechteckigen Darstellung der fraktalen Menge
	 * @param maxX: Groesster x-Wert der rechteckigen Darstellung der fraktalen Menge
	 * @param minY: Kleinster y-Wert der rechteckigen Darstellung der fraktalen Menge
	 * @param maxY: Groesster y-Wert der rechteckigen Darstellung der fraktalen Menge
	 * @param pixelProEinheit: Die Anzahl der Bildpunkte pro Längeneinheit
	 * @param display: Für die Konstanten DontDisplay oder Display
	 * @param save: Für die Konstanten DontSave oder Save
	 */

	public FraktaleMengeAusgabe (KomplexeZahl c,
			double minX, double maxX, double minY, double maxY, long pixelProEinheit,
			boolean display, boolean save)
	{
		super ("Fraktale Menge");
		this.c = c;
		this.mandelbrot = KomplexeZahl.betragsquadrat (c) == 0;
		this.minX = minX;
		this.maxX = maxX;
		this.minY = minY;
		this.maxY = maxY;
		this.pixelZahlX = (int) ((maxX - minX) * pixelProEinheit);
		this.pixelZahlY = (int) ((maxY - minY) * pixelProEinheit);
		this.display = display;
		this.save = save;
		this.setDefaultCloseOperation (javax.swing.WindowConstants.EXIT_ON_CLOSE);
	}

	// Method save for saving a fractal graphic to a graphical PNG file
	private void saveFile (java.awt.image.BufferedImage bufferedImage, java.lang.String fractalSetName)
	{
		final java.lang.String fileExtension = "png";
		java.util.Iterator <javax.imageio.ImageWriter> iter = javax.imageio.ImageIO.getImageWritersByFormatName (fileExtension);
		javax.imageio.ImageWriter imageWriter = (javax.imageio.ImageWriter) iter.next ();
		javax.imageio.ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam ();
		imageWriteParam.setCompressionMode (javax.imageio.ImageWriteParam.MODE_DISABLED);
		try
		{
			java.lang.String fileName = fractalSetName + "_" + this.minX + "_" + this.maxX + "_" + this.minY + "_" + this.maxY + "." + fileExtension;
			javax.imageio.stream.FileImageOutputStream fileImageOutputStream = new javax.imageio.stream.FileImageOutputStream (new java.io.File (fileName));
			imageWriter.setOutput (fileImageOutputStream);
			javax.imageio.IIOImage iIOimg = new javax.imageio.IIOImage ((java.awt.image.RenderedImage) bufferedImage, null, null);
			java.lang.System.out.println ("Save " + fileName);
			imageWriter.write (null, iIOimg, imageWriteParam);
		}
		catch (java.lang.Exception exception)
		{
			exception.printStackTrace ();
		}
	}

	// Methode init fuer die Speicherung und für die graphische Ausgabe einer Instanz von FraktaleMengeAusgabe als PNG-Datei.
	public void init ()
	{
		// Rahmengroesse, Hintergrund und das Layout werden gesetzt
		this.setSize (this.pixelZahlX, this.pixelZahlY);
		this.setBackground (java.awt.Color.WHITE);
		java.awt.image.BufferedImage bufferedImage = new java.awt.image.BufferedImage (this.pixelZahlX, this.pixelZahlY, java.awt.image.BufferedImage.TYPE_INT_RGB);
		java.awt.Graphics2D graphics2D = bufferedImage.createGraphics ();

		java.lang.String fractalSetName;
		if (this.mandelbrot)
		{
			fractalSetName = "Mandelbrot-Menge";
		}
		else
		{
			fractalSetName = "Julia-Menge" + "_" + this.c.re + "_" + this.c.im;
		}

		this.paint (graphics2D);

		if (this.save)
		{
			this.saveFile (bufferedImage, fractalSetName);
		}

		this.setVisible (this.display);
		java.lang.System.out.println ("Programm beendet.");
	}

	/*
	 * Zeichnet einen Punkt
	 * @param graphics Graphisches Element
	 * @param z: Variable z des Polynoms
	 * @param c: Variable c des Polynoms
	 * @param xWert Der X-Wert an dem der Punkt gemalt werden soll
	 * @param yWert Der Y-Wert an dem der Punkt gemalt werden soll
	 */
	private static void zeichnePunkt (java.awt.Graphics graphics, KomplexeZahl z, KomplexeZahl c, int xWert, int yWert)
	{
		int zahlZurFarbbestimmung = FraktaleMenge.iterationszahlBisSchranke (z, c);
		java.awt.Color farbe = bestimmeAusgabefarbe (zahlZurFarbbestimmung);
		graphics.setColor (farbe);
		graphics.drawLine (xWert, yWert, xWert, yWert);
	}

	/*
	 * Bestimmt die Farbe eines darzustellenden Punktes aus der Zahl der durchgefuehrten Iterationen in der Polynomfolge
	 * @param zahlZurFarbbestimmung: Zahl der durchgefuehrten Iterationen, für die eine Farbbestimmung durchgeführt werden aoll
	 * @return: Farbe vom Datentyp java.awt.Color
	 */
	private static java.awt.Color bestimmeAusgabefarbe (int zahlZurFarbbestimmung)
	{
		final int hell = 255;
		final int schwellfaktor1 = 8;
		final int schwellfaktor2 = 16;
		final int schwelle1 = hell / schwellfaktor1;
		final int schwelle2 = hell / schwellfaktor2;
		int helligkeit = hell * zahlZurFarbbestimmung / FraktaleMenge.maximaleAnzahlDerIterationen; // liegt im Intervall [0 .. HELL]
		java.awt.Color farbe;

		if (zahlZurFarbbestimmung < FraktaleMenge.maximaleAnzahlDerIterationen)
		{
			if (helligkeit > schwelle1) // Polynomfolge divergiert langsam
			{
				farbe = new java.awt.Color (hell, hell, helligkeit);					
			}
			else if (helligkeit > schwelle2) // Polynomfolge divergiert schneller
			{
				farbe = new java.awt.Color (schwellfaktor1 * helligkeit, helligkeit, helligkeit);
			}
			else // Polynomfolge divergiert schnell
			{
				farbe = new java.awt.Color (schwellfaktor2 * helligkeit, helligkeit, helligkeit);
			}
		}
		else // Polynomfolge wird als nicht divergent angesehen
		{
			farbe = java.awt.Color.black;
		}
		return farbe;
	}

	/*
	 * Berechnet den Skalierungsfaktor auf einer Koordinatenachse
	 * @param min: Der minimale Wert auf der Koordinatenachse
	 * @param max: Der maximale Wert auf der Koordinatenachse
	 * @param pixelZahl: Die gewuenschte Anzahl der Pixel auf der Koordinatenachse
	 * @return: Skalierungsfaktor auf der Koordinatenachse
	 */
	private static double berechneSkalierungsfaktor (double min, double max, long pixelZahl)
	{
		double skalierungsfaktor = (max - min) / pixelZahl;
		return skalierungsfaktor;
	}

	/*
	 * Transformiert eine Koordinate aus der Pixel-Ebene in die mathematische Ebene
	 * @param minX: Der minimale x-Wert in der mathematischen Ebene
	 * @param skalierungsfaktorX: Skalierungsfaktor von der Pixel-Ebene in die mathematische Ebene in x-Richtung
	 * @param pixelKoordinateX: Die x-Koordinate in der Pixel-Ebene
	 * @return: Die x-Koordinate in der mathematischen Ebene
	 */
	private static double koordinatentransformationX (double minX, double skalierungsfaktorX, long pixelKoordinateX)
	{
		double xKoordinate = minX + (pixelKoordinateX * skalierungsfaktorX);
		return xKoordinate;
	}

	/*
	 * Transformiert eine Koordinate aus der Pixel-Ebene in die mathematische Ebene
	 * @param maxY: Der maximale y-Wert auf der mathematischen Ebene
	 * @param skalierungsfaktorY: Skalierungsfaktor von der Pixel-Ebene in die mathematische Ebene in y-Richtung
	 * @param pixelKoordinateY: Die y-Koordinate in der Pixel-Ebene
	 * @return: Die y-Koordinate in der mathematischen Ebene
	 */
	private static double koordinatentransformationY (double maxY, double skalierungsfaktorY, long pixelKoordinateY)
	{
		double yKoordinate = maxY - (pixelKoordinateY * skalierungsfaktorY);
		return yKoordinate;
	}

	/*
	 * Zeichnet eine fraktale Menge (Julia-Menge oder Mandelbrot-Menge)
	 * @param fraktakeMenge: fraktale Menge
	 * @param graphics: Graphikelement
	 */
	private static void fraktaleMenge (FraktaleMengeAusgabe fraktaleMenge, java.awt.Graphics graphics)
	{
		final double skalierungX = berechneSkalierungsfaktor (fraktaleMenge.minX, fraktaleMenge.maxX, fraktaleMenge.pixelZahlX);
		final double skalierungY = berechneSkalierungsfaktor (fraktaleMenge.minY, fraktaleMenge.maxY, fraktaleMenge.pixelZahlY);

		KomplexeZahl c = fraktaleMenge.c; // Parameter c der fraktalen Menge
		KomplexeZahl z = new KomplexeZahl (0,0); // Parameter z der fraktalen Menge

		int ix; // Zaehler für Spalten in x-Richtung nach rechts
		int iy; // Zaehler für Zeilen in y-Richtung nach unten

		iy = 0; // oben starten
		while (iy < fraktaleMenge.pixelZahlY)
		{			
			ix = 0; // links starten
			while (ix < fraktaleMenge.pixelZahlX)
			{
				double re = koordinatentransformationX (fraktaleMenge.minX, skalierungX, ix);
				double im = koordinatentransformationY (fraktaleMenge.maxY, skalierungY, iy);
				if (fraktaleMenge.mandelbrot) // Mandelbrot-Menge
				{
					z.re = 0; // Realteil von z0 ist immer gleich 0
					z.im = 0; // Imaginaerteil von z0 ist immer gleich 0
					c.re = re; // Realteil von c ist immer gleich der x-Koordinate
					c.im = im; // Imaginaerteil von c ist immer gleich der y-Koordinate
				}
				else // Julia-Menge
				{
					z.re = re; // Realteil von z ist immer gleich der x-Koordinate
					z.im = im; // Imaginaerteil von z ist immer gleich der y-Koordinate	
				}
				zeichnePunkt (graphics, z, c, ix, iy);
				ix++;
			}
			iy++;
		}
	}

	/*
	 * Zeichnet eine fraktale Menge
	 * @param graphics Graphisches Element
	 */
	public void paint (java.awt.Graphics graphics)
	{
		// Rahmengröße, Hintergrund und das Layout werden gesetzt.
		fraktaleMenge (this, graphics);
	}
}