diff --git a/src/main/java/schule/ngb/zm/Color.java b/src/main/java/schule/ngb/zm/Color.java index 33c2033..e7774fd 100644 --- a/src/main/java/schule/ngb/zm/Color.java +++ b/src/main/java/schule/ngb/zm/Color.java @@ -3,17 +3,15 @@ package schule.ngb.zm; /** * Repräsentiert eine Farbe in der Zeichenmaschine. *
- * Farben bestehen entweder aus einem Grauwert (zwischen 0 und - * 255) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen - * 0 und 255). + * Farben bestehen entweder aus einem Grauwert (zwischen 0 und 255) oder einem + * Rot-, Grün- und Blauanteil (jeweils zwischen 0 und 255). *
- * Eine Farbe hat außerdem einen Transparenzwert zwischen 0 - * (unsichtbar) und 255 (deckend). + * Eine Farbe hat außerdem einen Transparenzwert zwischen 0 (unsichtbar) und 255 + * (deckend). */ public class Color { - //@formatter:off /** * Die Farbe Schwarz (Grauwert 0). @@ -124,12 +122,12 @@ public class Color { /** * Erstellt eine Farbe. Die Parameter red, green und - * blue geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte - * liegen zwischen 0 und 255. + * blue geben die Rot-, Grün- und Blauanteile der Farbe. Die + * Werte liegen zwischen 0 und 255. * - * @param red Rotwert zwischen 0 und 255. + * @param red Rotwert zwischen 0 und 255. * @param green Grünwert zwischen 0 und 255. - * @param blue Blauwert zwischen 0 und 255. + * @param blue Blauwert zwischen 0 und 255. */ public Color( int red, int green, int blue ) { this(red, green, blue, 255); @@ -137,20 +135,18 @@ public class Color { /** * Erstellt eine Farbe. Die Parameter red, green und - * blue geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte - * liegen zwischen 0 und 255. + * blue geben die Rot-, Grün- und Blauanteile der Farbe. Die + * Werte liegen zwischen 0 und 255. * alpha gibt den den Transparentwert an (auch zwischen - * 0 und 255), wobei - * 0 komplett durchsichtig ist und 255 komplett - * deckend. + * 0 und 255), wobei 0 komplett durchsichtig ist und 255 komplett deckend. * - * @param red Rotwert zwischen 0 und 255. + * @param red Rotwert zwischen 0 und 255. * @param green Grünwert zwischen 0 und 255. - * @param blue Blauwert zwischen 0 und 255. + * @param blue Blauwert zwischen 0 und 255. * @param alpha Transparentwert zwischen 0 und 255. */ public Color( int red, int green, int blue, int alpha ) { - rgba = (alpha << 24) | (red << 16) | (green << 8) | blue; + rgba = ((alpha&0xFF) << 24) | ((red&0xFF) << 16) | ((green&0xFF) << 8) | ((blue&0xFF) << 0); } /** @@ -184,10 +180,11 @@ public class Color { /** * Interner Konstruktor für die Initialisierung einer Farbe mit einem * RGBA-Wert. - * + *
* Da der Konstruktor {@link #Color(int)} schon besetzt ist, muss hier der - * Parameter {@code isRGBA} auf {@code true} gesetzt werden, damit {@code rgba} - * korrekt interpretiert wird. + * Parameter {@code isRGBA} auf {@code true} gesetzt werden, damit + * {@code rgba} korrekt interpretiert wird. + * * @param rgba RGBA-wert der Farbe. * @param isRGBA Sollte immer {@code true} sein. */ @@ -210,12 +207,12 @@ public class Color { return c; } - public static Color getHSBColor(double h, double s, double b) { - return new Color(java.awt.Color.getHSBColor((float)h, (float)s, (float)b)); + public static Color getHSBColor( double h, double s, double b ) { + return new Color(java.awt.Color.getHSBColor((float) h, (float) s, (float) b)); } - public static Color getHSLColor(double h, double s, double l) { - int rgb = Color.HSLtoRGB(new float[]{(float)h, (float)s, (float)l}); + public static Color getHSLColor( double h, double s, double l ) { + int rgb = Color.HSLtoRGB(new float[]{(float) h, (float) s, (float) l}); return Color.getRGBColor(rgb); } @@ -235,9 +232,9 @@ public class Color { } /** - * Erzeugt eine Farbe aus einem hexadezimalen Code. Der Hexcode kann - * sechs- oder achtstellig sein (wenn ein Transparentwert vorhanden ist). - * Dem Code kann ein {@code #} Zeichen vorangestellt sein. + * Erzeugt eine Farbe aus einem hexadezimalen Code. Der Hexcode kann sechs- + * oder achtstellig sein (wenn ein Transparentwert vorhanden ist). Dem Code + * kann ein {@code #} Zeichen vorangestellt sein. * * @param hexcode * @return @@ -336,10 +333,11 @@ public class Color { /** * Konvertiert eine Farbe mit Komponenten im HSL-Farbraum in den * RGB-Farbraum. - * + *
* Die Farbkomponenten werden als Float-Array übergeben. Im Index 0 steht * der h-Wert im Bereich 0 bis 360, Index 1 und 2 enthalten den s- und * l-Wert im Bereich von 0 bis 1. + * * @param hsl Die Farbkomponenten im HSL-Farbraum. * @param alpha Ein Transparenzwert im Bereich 0 bis 255. * @return Der RGBA-Wert der Farbe. @@ -392,6 +390,7 @@ public class Color { /** * Erzeugt eine Kopie dieser Farbe. + * * @return Ein neues Farbobjekt. */ public Color copy() { @@ -400,10 +399,11 @@ public class Color { /** * Gibt den RGBA-Wert dieser Farbe zurück. - * - * Eine Farbe wird als 32-Bit Integer gespeichert. Bits 24-31 enthalten - * den Transparenzwert, 16-23 den Rotwert, 8-15 den Grünwert und 0-7 den + *
+ * Eine Farbe wird als 32-Bit Integer gespeichert. Bits 24-31 enthalten den + * Transparenzwert, 16-23 den Rotwert, 8-15 den Grünwert und 0-7 den * Blauwert der Farbe. + * * @return Der RGBA-Wert der Farbe. * @see #getRed() * @see #getGreen() @@ -416,6 +416,7 @@ public class Color { /** * Gibt den Rotwert dieser Farbe zurück. + * * @return Der Rotwert der Farbe zwischen 0 und 255. */ public int getRed() { @@ -424,6 +425,7 @@ public class Color { /** * Gibt den Grünwert dieser Farbe zurück. + * * @return Der Grünwert der Farbe zwischen 0 und 255. */ public int getGreen() { @@ -432,6 +434,7 @@ public class Color { /** * Gibt den Blauwert dieser Farbe zurück. + * * @return Der Blauwert der Farbe zwischen 0 und 255. */ public int getBlue() { @@ -440,6 +443,7 @@ public class Color { /** * Gibt den Transparenzwert dieser Farbe zurück. + * * @return Der Transparenzwert der Farbe zwischen 0 und 255. */ public int getAlpha() { @@ -448,9 +452,10 @@ public class Color { /** * Erzeugt ein {@link java.awt.Color}-Objekt aus dieser Farbe. + *
+ * Das erzeugte Farbobjekt hat dieselben Rot-, Grün-, Blau- und + * Transparenzwerte wie diese Farbe. * - * Das erzeugte Farbobjekt hat dieselben Rot-, Grün-, Blau- - * und Transparenzwerte wie diese Farbe. * @return Ein Java-Farbobjekt. */ public java.awt.Color getJavaColor() { @@ -468,11 +473,18 @@ public class Color { * @return {@code true}, wenn die Objekte gleich sind, sonst {@code false}. */ public boolean equals( Object obj ) { - return obj instanceof Color && ((Color)obj).getRGBA() == this.rgba; + if( obj == null ) { return false; } + if( obj instanceof Color ) { + return ((Color) obj).getRGBA() == this.rgba; + } else if( obj instanceof java.awt.Color ) { + return ((java.awt.Color) obj).getRGB() == this.rgba; + } + return false; } /** * Erzeugt einen Text-String, der diese Farbe beschreibt. + * * @return Eine Textversion der Farbe. */ @Override @@ -482,6 +494,7 @@ public class Color { /** * Berechnet einen Hashcode für dieses Farbobjekt. + * * @return Ein Hashcode für diese Rabe. */ @Override @@ -490,7 +503,8 @@ public class Color { } /** - * Erzeugt eine um 30% hellere Version dieser Farbe. + * Erzeugt eine um 30% hellere Version dieser Farbe. + * * @return Ein Farbobjekt mit einer helleren Farbe. */ public Color brighter() { @@ -499,6 +513,7 @@ public class Color { /** * Erzeugt eine um {@code percent} hellere Version dieser Farbe. + * * @param percent Eine Prozentzahl zwischen 0 und 100. * @return Ein Farbobjekt mit einer helleren Farbe. */ @@ -510,6 +525,7 @@ public class Color { /** * Erzeugt eine um 30% dunklere Version dieser Farbe. + * * @return Ein Farbobjekt mit einer dunkleren Farbe. */ public Color darker() { @@ -518,6 +534,7 @@ public class Color { /** * Erzeugt eine um {@code percent} dunklere Version dieser Farbe. + * * @param percent Eine Prozentzahl zwischen 0 und 100. * @return Ein Farbobjekt mit einer dunkleren Farbe. */ @@ -528,11 +545,12 @@ public class Color { } public Color greyscale() { - return new Color((int)(getRed()*.299 + getGreen()*.587 + getBlue()*0.114)); + return new Color((int) (getRed() * .299 + getGreen() * .587 + getBlue() * 0.114)); } /** * Erzeugt eine zu dieser invertierte Farbe. + * * @return Ein Farbobjekt mit der invertierten Farbe. */ public Color inverted() { @@ -542,6 +560,7 @@ public class Color { /** * Erzeugt die Komplementärfarbe zu dieser. + * * @return Ein Farbobjekt mit der Komplementärfarbe. */ public Color complement() { @@ -554,11 +573,12 @@ public class Color { * Wählt entweder {@link #WHITE weiß} oder {@link #BLACK schwarz} aus, je * nachdem, welche der Farben besser als Textfarbe mit dieser Farbe als * Hintergrund funktioniert (besser lesbar ist). + * * @return Schwarz oder weiß. */ public Color textcolor() { // Basiert auf https://stackoverflow.com/questions/946544/good-text-foreground-color-for-a-given-background-color - if( (getRed()*.299 + getGreen()*.587 + getBlue()*0.114) < 186 ) { + if( (getRed() * .299 + getGreen() * .587 + getBlue() * 0.114) < 186 ) { return WHITE; } else { return BLACK; diff --git a/src/main/java/schule/ngb/zm/Constants.java b/src/main/java/schule/ngb/zm/Constants.java index 561b3aa..b9436f0 100644 --- a/src/main/java/schule/ngb/zm/Constants.java +++ b/src/main/java/schule/ngb/zm/Constants.java @@ -1,6 +1,7 @@ package schule.ngb.zm; import schule.ngb.zm.util.ImageLoader; +import schule.ngb.zm.util.Noise; import java.awt.Cursor; import java.awt.event.KeyEvent; @@ -8,6 +9,39 @@ import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.Random; +/** + * Basisklasse für die meisten Objekte der Zeichemaschine, die von Nutzern + * erweitert werden können. + *
+ * Die Konstanten stellen viele Funktionen zur einfachen Programmierung bereit + * und enthält auch einige dynamische Werte, die von der Zeichenmaschine laufend + * aktuell gehalten werden (beispielsweise {@link #runtime}. + *
+ * Für die Implementierung eigener Klassen ist es oft hilfreich von
+ * {@code Constants} zu erben, um die Methoden und Konstanten einfach im
+ * Programm nutzen zu können.
+ *
+ *
+ * class MyClass extends Constants {
+ * public int summe( int a, int b ) {
+ * // sum ist durch Vererbung verfügbar,
+ * return sum(a, b);
+ * }
+ * }
+ *
+ * Alternativ können die statischen Klassenmethoden auch direkt genutzt werden:
+ *
+ *
+ * Constants.sum(1,2,3,4); // 10
+ *
+ * Oder die Methoden statisch importiert werden:
+ *
+ */
+@SuppressWarnings( "unused" )
public class Constants {
/**
@@ -76,162 +110,287 @@ public class Constants {
*/
public static final int STD_BUFFER = 10;
+ /**
+ * Option für durchgezogene Konturen und Linien.
+ */
public static final Options.StrokeType SOLID = Options.StrokeType.SOLID;
+ /**
+ * Option für gestrichelte Konturen und Linien.
+ */
public static final Options.StrokeType DASHED = Options.StrokeType.DASHED;
+ /**
+ * Option für gepunktete Konturen und Linien.
+ */
public static final Options.StrokeType DOTTED = Options.StrokeType.DOTTED;
+ /**
+ * Option für Pfeile mit Strichen als Kopf.
+ */
public static final Options.ArrowHead LINES = Options.ArrowHead.LINES;
+ /**
+ * Option für Pfeile mit gefüllten Köpfen.
+ */
public static final Options.ArrowHead FILLED = Options.ArrowHead.FILLED;
+ /**
+ * Option für den Abschluss eines Pfades oder Bogens, ohne die Enden zu
+ * verbinden.
+ */
public static final Options.PathType OPEN = Options.PathType.OPEN;
+ /**
+ * Option für den Abschluss eines Pfades oder Bogens durch Verbindung der
+ * Enden des Bogens mit einer Linie.
+ */
public static final Options.PathType CLOSED = Options.PathType.CLOSED;
+ /**
+ * Option für den Abschluss eines Bogens durch Verbindung der Enden des
+ * Bogens mit dem Mittelpunkt der zugrundeliegenden Ellipse. Dadurch
+ * entsteht ein Kreisausschnitt.
+ */
public static final Options.PathType PIE = Options.PathType.PIE;
+ /**
+ * Richtung: Mitte (bzw. keine Richtung)
+ */
public static final Options.Direction CENTER = Options.Direction.CENTER;
+ /**
+ * Richtung: Norden
+ *
+ * @see #UP
+ */
public static final Options.Direction NORTH = Options.Direction.NORTH;
+ /**
+ * Richtung: Osten
+ *
+ * @see #RIGHT
+ */
public static final Options.Direction EAST = Options.Direction.EAST;
+ /**
+ * Richtung: Süden
+ *
+ * @see #DOWN
+ */
public static final Options.Direction SOUTH = Options.Direction.SOUTH;
+ /**
+ * Richtung: Westen
+ *
+ * @see #LEFT
+ */
public static final Options.Direction WEST = Options.Direction.WEST;
+ /**
+ * Richtung: Nordosten
+ *
+ * @see #UPLEFT
+ */
public static final Options.Direction NORTHEAST = Options.Direction.NORTHEAST;
+ /**
+ * Richtung: Südosten
+ *
+ * @see #DOWNLEFT
+ */
public static final Options.Direction SOUTHEAST = Options.Direction.SOUTHEAST;
+ /**
+ * Richtung: Nordwesten
+ *
+ * @see #UPRIGHT
+ */
public static final Options.Direction NORTHWEST = Options.Direction.NORTHWEST;
+ /**
+ * Richtung: Südwesten
+ *
+ * @see #DOWNRIGHT
+ */
public static final Options.Direction SOUTHWEST = Options.Direction.SOUTHWEST;
+ /**
+ * Richtung: Mitte
+ *
+ * @see #CENTER
+ */
public static final Options.Direction MIDDLE = Options.Direction.MIDDLE;
+ /**
+ * Richtung: Oben
+ *
+ * @see #NORTH
+ */
public static final Options.Direction UP = Options.Direction.UP;
+ /**
+ * Richtung: Rechts
+ *
+ * @see #EAST
+ */
public static final Options.Direction RIGHT = Options.Direction.RIGHT;
+ /**
+ * Richtung: Unten
+ *
+ * @see #SOUTH
+ */
public static final Options.Direction DOWN = Options.Direction.DOWN;
+ /**
+ * Richtung: Links
+ *
+ * @see #WEST
+ */
public static final Options.Direction LEFT = Options.Direction.LEFT;
/**
- * Schwarz
+ * Richtung: Oben links
+ *
+ * @see #NORTHWEST
+ */
+ public static final Options.Direction UPLEFT = Options.Direction.UPLEFT;
+
+ /**
+ * Richtung: Unten links
+ *
+ * @see #SOUTHWEST
+ */
+ public static final Options.Direction DOWNLEFT = Options.Direction.DOWNLEFT;
+
+ /**
+ * Richtung: Oben rechts
+ *
+ * @see #UPRIGHT
+ */
+ public static final Options.Direction UPRIGHT = Options.Direction.UPRIGHT;
+
+ /**
+ * Richtung: Unten rechts
+ *
+ * @see #SOUTHEAST
+ */
+ public static final Options.Direction DOWNRIGHT = Options.Direction.DOWNRIGHT;
+
+
+ /**
+ * Farbe: Schwarz
*/
public static final Color BLACK = Color.BLACK;
/**
- * Weiß
+ * Farbe: Weiß
*/
public static final Color WHITE = Color.WHITE;
/**
- * Grau
+ * Farbe: Grau
*/
public static final Color GRAY = Color.GRAY;
/**
- * Dunkelgrau
+ * Farbe: Dunkelgrau
*/
public static final Color DARKGRAY = Color.DARKGRAY;
/**
- * Hellgrau
+ * Farbe: Hellgrau
*/
public static final Color LIGHTGRAY = Color.LIGHTGRAY;
/**
- * Rot
+ * Farbe: Rot
*/
public static final Color RED = Color.RED;
/**
- * Blau
+ * Farbe: Blau
*/
public static final Color BLUE = Color.BLUE;
/**
- * Grün
+ * Farbe: Grün
*/
public static final Color GREEN = Color.GREEN;
/**
- * Gelb
+ * Farbe: Gelb
*/
public static final Color YELLOW = Color.YELLOW;
/**
- * Orange
+ * Farbe: Orange
*/
public static final Color ORANGE = Color.ORANGE;
/**
- * Türkis
+ * Farbe: Türkis
*/
public static final Color CYAN = Color.CYAN;
/**
- * Magenta
+ * Farbe: Magenta
*/
public static final Color MAGENTA = Color.MAGENTA;
/**
- * Pink
+ * Farbe: Pink
*/
public static final Color PINK = Color.PINK;
/**
- * Lila
+ * Farbe: Lila
*/
public static final Color PURPLE = Color.PURPLE;
/**
- * Braun
+ * Farbe: Braun
*/
public static final Color BROWN = Color.BROWN;
/**
* Standardfarbe für den Hintergrund.
*/
- public static final Color STD_BACKGROUND = new Color(200, 200, 200);
+ public static Color DEFAULT_BACKGROUND = new Color(200, 200, 200);
/**
- * Konstante zur Prüfung, ob ein Mausknopf gedrückt wurde.
+ * Konstante zur Prüfung, ob kein Mausknopf gedrückt wurde.
*/
- public static final int NOBUTTON = MouseEvent.NOBUTTON;
+ public static final int NOMOUSE = MouseEvent.NOBUTTON;
/**
* Konstante zur Prüfung, ob Mausknopf 1 (links) gedrückt wurde.
*/
- public static final int BUTTON1 = MouseEvent.BUTTON1;
+ public static final int MOUSE1 = MouseEvent.BUTTON1;
/**
* Konstante zur Prüfung, ob Mausknopf 2 (rechts) gedrückt wurde.
*/
- public static final int BUTTON2 = MouseEvent.BUTTON2;
+ public static final int MOUSE2 = MouseEvent.BUTTON2;
/**
* Konstante zur Prüfung, ob Mausknopf 3 (mittig) gedrückt wurde.
*/
- public static final int BUTTON3 = MouseEvent.BUTTON3;
+ public static final int MOUSE3 = MouseEvent.BUTTON3;
/**
- * Konstante für die Kreiszahl Pi.
+ * Konstante für die Kreiszahl Pi (entspricht 180 Grad).
*/
public static final double PI = Math.PI;
/**
- * Konstante für die Hälfte der Kreiszahl Pi.
+ * Konstante für die Hälfte der Kreiszahl Pi (entspricht 90 Grad).
*/
public static final double HALF_PI = Math.PI / 2.0;
/**
- * Konstante für ein Viertel der Kreiszahl Pi.
+ * Konstante für ein Viertel der Kreiszahl Pi (entspricht 45 Grad).
*/
public static final double QUARTER_PI = Math.PI / 4.0;
@@ -240,8 +399,7 @@ public class Constants {
*/
public static final double TWO_PI = Math.PI * 2.0;
-
- /**
+ /*
* Globale Variablen, die von allen Klassen genutzt werden dürfen. Änderungen
* wirken sich auf die aktuelle Zeichenmaschine aus und sollten nur von der
* Zeichenmaschine selbst vorgenommen werden.
@@ -268,70 +426,86 @@ public class Constants {
public static double delta = 0.0;
/**
- * Die aktuelle {@code x}-Koordinate der Maus.
+ * Die aktuelle {@code x}-Koordinate der Maus. (Wird einmal pro Frame
+ * aktualisiert.)
*/
public static double mouseX = 0.0;
/**
- * Die aktuelle {@code y}-Koordinate der Maus.
+ * Die aktuelle {@code y}-Koordinate der Maus. (Wird einmal pro Frame
+ * aktualisiert.)
*/
public static double mouseY = 0.0;
/**
- * Die letzte {@code x}-Koordinate der Maus (wird einmal pro Frame
+ * Die letzte {@code x}-Koordinate der Maus. (Wird einmal pro Frame
* aktualisiert).
*/
public static double pmouseX = 0.0;
/**
- * Die letzte {@code y}-Koordinate der Maus (wird einmal pro Frame
- * aktualisiert).
+ * Die letzte {@code y}-Koordinate der Maus. (Wird einmal pro Frame
+ * aktualisiert.)
*/
public static double pmouseY = 0.0;
/**
- * Die aktuelle (current) {@code x}-Koordinate der Maus
- * (wird bei jeder Mausbewegung aktualisiert).
+ * Die aktuelle (current) {@code x}-Koordinate der Maus. (Wird bei
+ * jeder Mausbewegung aktualisiert).
*/
public static double cmouseX = 0.0;
/**
- * Die aktuelle (current) {@code y}-Koordinate der Maus
- * (wird bei jeder Mausbewegung aktualisiert).
+ * Die aktuelle (current) {@code y}-Koordinate der Maus. (Wird bei
+ * jeder Mausbewegung aktualisiert).
*/
public static double cmouseY = 0.0;
/**
- * Gibt an, ob ein Mausknopf derzeit gedrückt ist.
+ * Gibt an, ob derzeit ein Mausknopf gedrückt ist.
*/
public static boolean mousePressed = false;
/**
* Der aktuell gedrückte Mausknopf. Die Mausknöpfe werden durch die
- * Konstanten {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2} und
- * {@link #BUTTON3} angegeben. (Sie stimmen mit den Konstanten in
- * {@link MouseEvent} überein.
- *
- * @see MouseEvent
+ * Konstanten {@link #NOMOUSE}, {@link #MOUSE1}, {@link #MOUSE2} und
+ * {@link #MOUSE3} angegeben.
*/
- public static int mouseButton = NOBUTTON;
+ public static int mouseButton = NOMOUSE;
/**
* Das zuletzt ausgelöste {@code MouseEvent}.
*/
public static MouseEvent mouseEvent;
- // Mauszeiger
+ /**
+ * Mauszeiger: Pfeil
+ */
public static final int ARROW = Cursor.DEFAULT_CURSOR;
+ /**
+ * Mauszeiger: Fadenkreuz
+ */
public static final int CROSS = Cursor.CROSSHAIR_CURSOR;
+ /**
+ * Mauszeiger: Hand
+ */
public static final int HAND = Cursor.HAND_CURSOR;
+ /**
+ * Mauszeiger: Bewegungspfeile
+ */
public static final int MOVE = Cursor.MOVE_CURSOR;
+ /**
+ * Mauszeiger: Textzeiger
+ */
public static final int TEXT = Cursor.TEXT_CURSOR;
+ /**
+ * Mauszeiger: Ladezeiger
+ */
public static final int WAIT = Cursor.WAIT_CURSOR;
/**
@@ -340,14 +514,16 @@ public class Constants {
public static boolean keyPressed = false;
/**
- * Das Zeichen der zuletzt gedrückten Taste.
+ * Das Text-Zeichen der zuletzt gedrückten Taste. Für Tasten ohne
+ * zugeordnetes Zeichen ist das Zeichen leer.
*/
public static char key = ' ';
/**
* Der Tastencode der zuletzt gedrückten Taste. Die Keycodes können in der
- * Klasse {@link KeyEvent} nachgesehen werden. (Zum Beispiel
- * {@link KeyEvent#VK_ENTER}.)
+ * Klasse {@link KeyEvent} nachgesehen werden. Die Keycodes für die
+ * wichtigsten Tasten sind als Konstanten mit dem Prefix {@code KEY_}
+ * vorhanden. (Zum Beispiel {@link #KEY_A}.)
*/
public static int keyCode = 0;
@@ -359,12 +535,12 @@ public class Constants {
/**
* Die Höhe der Zeichenleinwand.
*/
- public static int width;
+ public static int canvasWidth;
/**
* Die Breite der Zeichenleinwand.
*/
- public static int height;
+ public static int canvasHeight;
/**
* Die Breite des Bildschirms, auf dem das Zeichenfenster geöffnet wurde.
@@ -387,8 +563,11 @@ public class Constants {
/**
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
- * zwischen 0 und 255 an, wobei
- * 0 schwarz und 255 weiß ist.
+ * zwischen 0 und 255 an, wobei 0 schwarz und 255 weiß ist.
+ *
+ *
+ * import static Constants.*;
+ *
+ * sum(1, 2, 3, 4); // 10
+ *
+ * Color iron_grey = color(94);
+ *
*
* @param gray Grauwert zwischen 0 und 255.
* @return Ein passendes Farbobjekt.
@@ -399,16 +578,16 @@ public class Constants {
/**
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
- * zwischen 0 und 255 an, wobei
- * 0 schwarz und 255 weiß ist.
- * {@code alpha} gibt den den Transparentwert an (auch zwischen
- * 0 und 255), wobei
- * 0 komplett durchsichtig ist und 255 komplett
- * deckend.
+ * zwischen 0 und 255 an, wobei 0 schwarz und 255 weiß ist. {@code alpha}
+ * gibt den Transparenzwert an (auch zwischen 0 und 255), wobei 0 komplett
+ * durchsichtig ist und 255 komplett deckend.
+ *
+ *
+ * Color iron_grey_50 = color(94, 50);
+ *
*
* @param gray Grauwert zwischen 0 und 255.
- * @param alpha Transparentwert zwischen 0 und
- * 255.
+ * @param alpha Transparenzwert zwischen 0 und 255.
* @return Ein passendes Farbobjekt.
*/
public static final Color color( int gray, int alpha ) {
@@ -420,6 +599,10 @@ public class Constants {
* {@code blue} geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
* liegen zwischen 0 und 255.
*
+ *
+ * Color arctic_blue = color(149, 214, 220);
+ *
+ *
* @param red Rotwert zwischen 0 und 255.
* @param green Grünwert zwischen 0 und 255.
* @param blue Blauwert zwischen 0 und 255.
@@ -432,17 +615,18 @@ public class Constants {
/**
* Erstellt eine Farbe. Die Parameter {@code red}, {@code green} und
* {@code blue} geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
- * liegen zwischen 0 und 255. {@code alpha} gibt
- * den den Transparentwert an (auch zwischen 0 und
- * 255), wobei
- * 0 komplett durchsichtig ist und 255 komplett
- * deckend.
+ * liegen zwischen 0 und 255. {@code alpha} gibt den Transparenzwert an
+ * (auch zwischen 0 und 255), wobei 0 komplett durchsichtig ist und 255
+ * komplett deckend.
+ *
+ *
+ * Color arctic_blue_50 = color(149, 214, 220, 50);
+ *
*
* @param red Rotwert zwischen 0 und 255.
* @param green Grünwert zwischen 0 und 255.
* @param blue Blauwert zwischen 0 und 255.
- * @param alpha Transparenzwert zwischen 0 und
- * 255.
+ * @param alpha Transparenzwert zwischen 0 und 255.
* @return Ein passendes Farbobjekt.
*/
public static final Color color( int red, int green, int blue, int alpha ) {
@@ -455,6 +639,10 @@ public class Constants {
* Alle Farbkomponenten (Rot, Grün und Blau) werden zufällig im Bereich 0
* bis 255 gewählt.
*
+ *
+ * Color fillColor = randomColor();
+ *
+ *
* @return Ein zufälliges Farbobjekt.
*/
public static final Color randomColor() {
@@ -465,6 +653,10 @@ public class Constants {
* Erzeugt eine "hübsche" zufällige Farbe. Die Farbe wird so gewählt, dass
* die Farben nicht zu verwaschen oder dunkel wirken.
*
+ *
+ * Color fillColor = randomNiceColor();
+ *
+ *
* @return Ein zufälliges Farbobjekt.
*/
public static final Color randomNiceColor() {
@@ -480,6 +672,10 @@ public class Constants {
* und die Sättigung (saturation) und Hellwert (brightness) im Bereich 0 bis
* 100.
*
+ *
+ * Color arctic_blue = colorHsl(185, 32, 86);
+ *
+ *
* @param h Farbton im Bereich 0 bis 360.
* @param s Sättigung im Bereich 0 bis 100.
* @param b Hellwert im Bereich 0 bis 100.
@@ -498,6 +694,10 @@ public class Constants {
* und die Sättigung (saturation) und Helligkeit (lightness) im Bereich 0
* bis 100.
*
+ *
+ * Color arctic_blue = colorHsl(185, 50, 72);
+ *
+ *
* @param h Farbton im Bereich 0 bis 360.
* @param s Sättigung im Bereich 0 bis 100.
* @param l Helligkeit im Bereich 0 bis 100.
@@ -509,14 +709,27 @@ public class Constants {
return Color.getHSLColor(h % 360.0, s / 100.0, l / 100.0);
}
- public static final BufferedImage loadImage( String name ) {
- return ImageLoader.loadImage(name);
+ // Ressourcen
+
+ /**
+ * Lädt ein Bild aus einer Datei oder von einer Webadresse.
+ *
+ * @param source Ein Dateipfad oder eine Webadresse.
+ * @return Das geladene Bild.
+ * @see ImageLoader#loadImage(String)
+ */
+ public static final BufferedImage loadImage( String source ) {
+ return ImageLoader.loadImage(source);
}
// Mathematische Funktionen
/**
- * Berechnet das Minimum aller übergebenen Werte.
+ * Berechnet das Minimum aller angegebenen Werte.
+ *
+ *
+ * double minimum = min(1.0, 5.1, 3.2); // 1.0
+ *
*
* @param numbers Die Werte, aus denen das Minimum ermittelt werden soll.
* @return Das Minimum der Werte.
@@ -536,7 +749,11 @@ public class Constants {
}
/**
- * Berechnet das Maximum aller übergebenen Werte.
+ * Berechnet das Maximum aller angegebenen Werte.
+ *
+ *
+ * double maximum = max(1.0, 5.1, 3.2); // 5.1
+ *
*
* @param numbers Die Werte, aus denen das Maximum ermittelt werden soll.
* @return Das Maximum der Werte.
@@ -555,18 +772,39 @@ public class Constants {
return max;
}
+ /**
+ * Berechnet die Summe alle angegebenen Werte.
+ *
+ *
+ * double summe = sum(1.0, 2.0, 3.2); // 6.2
+ *
+ *
+ * @param numbers Die Werte, aus denen die Summe berechnet werden soll.
+ * @return Die Summe der Werte.
+ */
public static final double sum( double... numbers ) {
if( numbers == null ) {
throw new IllegalArgumentException("Array may not be
+ * double summe = sum(1.0, 2.2, 3.1); // 2.1
+ *
+ *
+ * @param numbers Die Werte, aus denen der MIttelwert berechnet werden
+ * soll.
+ * @return Der Mittelwert der Werte.
+ */
public static final double avg( double... numbers ) {
if( numbers == null || numbers.length == 0 ) {
throw new IllegalArgumentException("Array may not be
+ * double positiv = abs(-3.2); // 3.2
+ *
*
* @param x Eine Zahl.
* @return Der Absolutbetrag.
@@ -586,7 +828,11 @@ public class Constants {
}
/**
- * Ermittelt das Vorzeichen der Zahl {@code x}.
+ * Ermittelt das Vorzeichen der angegebenen Zahl.
+ *
+ *
+ * double vorzeichen = sign(-3.2); // -1.0
+ *
*
* @param x Eine Zahl.
* @return -1, 1 oder 0.
@@ -596,7 +842,11 @@ public class Constants {
}
/**
- * Rundet die Zahl {@code x}.
+ * Rundet die angegebene Zahl auf die nächste ganze Zahl.
+ *
+ *
+ * double gerundet = sign(3.2); // 3.0
+ *
*
* @param x Eine Zahl.
* @return Die gerundete Zahl.
@@ -606,7 +856,11 @@ public class Constants {
}
/**
- * Rundet die Zahl {@code x} ab.
+ * Rundet die angegebene Zahl ab.
+ *
+ *
+ * double abgerundet = sign(3.2); // 3.0
+ *
*
* @param x Eine Zahl.
* @return Die abgerundete Zahl.
@@ -616,7 +870,11 @@ public class Constants {
}
/**
- * Rundet die Zahl {@code x} auf.
+ * Rundet die angegebene Zahl auf.
+ *
+ *
+ * double aufgerundet = sign(3.2); // 4.0
+ *
*
* @param x Eine Zahl.
* @return Die aufgerundete Zahl.
@@ -626,7 +884,11 @@ public class Constants {
}
/**
- * Ermittelt die Quadratwurzel der Zahl {@code x}.
+ * Ermittelt die Quadratwurzel der angegebenen Zahl.
+ *
+ *
+ * double wurzel = sqrt(16); // 4.0
+ *
*
* @param x Eine Zahl.
* @return Die Quadratwurzel.
@@ -636,19 +898,27 @@ public class Constants {
}
/**
- * Ermittelt die Potenz der Zahl {@code x} zum Exponenten {@code p}.
+ * Ermittelt die Potenz der angegebenen Zahl zum angegebenen Exponenten.
+ *
+ *
+ * double hoch4 = sqrt(8, 4); // 4096.0
+ *
*
* @param x Eine Zahl.
- * @param p Der Exponent.
- * @return {@code x} hoch {@code p}.
+ * @param e Der Exponent.
+ * @return {@code x} hoch {@code e}.
*/
- public static final double pow( double x, double p ) {
- return Math.pow(x, p);
+ public static final double pow( double x, double e ) {
+ return Math.pow(x, e);
}
/**
* Rechnet von Grad in Radian um.
*
+ *
+ * double radian = radians(360); // 6.28318530717959
+ *
+ *
* @param angle Ein Winkel in Grad.
* @return Der Winkel in Radian.
*/
@@ -657,7 +927,11 @@ public class Constants {
}
/**
- * Rechent von Radian in Grad um.
+ * Rechnet von Radian in Grad um.
+ *
+ *
+ * double grad = radians(HALF_PI); // 90.0
+ *
*
* @param radians Der Winkel in Radian.
* @return Der Winkel in Grad.
@@ -667,7 +941,11 @@ public class Constants {
}
/**
- * Ermittelt den Sinus der Zahl {@code x}.
+ * Ermittelt den Sinus der angegebenen Zahl.
+ *
+ *
+ * double sinus = sin(0.0); // 0.0
+ *
*
* @param x Eine Zahl.
* @return {@code sin(x)}.
@@ -677,7 +955,11 @@ public class Constants {
}
/**
- * Ermittelt den Kosinus der Zahl {@code x}.
+ * Ermittelt den Kosinus der angegebenen Zahl.
+ *
+ *
+ * double kosinus = cos(0.0); // 1.0
+ *
*
* @param x Eine Zahl.
* @return {@code cos(x)}.
@@ -687,7 +969,11 @@ public class Constants {
}
/**
- * Ermittelt den Tangens der Zahl {@code x}.
+ * Ermittelt den Tangens der angegebenen Zahl.
+ *
+ *
+ * double sinus = tan(1.0); // 1.5574077246549
+ *
*
* @param x Eine Zahl.
* @return {@code tan(x)}.
@@ -697,7 +983,7 @@ public class Constants {
}
/**
- * Ermittelt den Arkussinus der Zahl {@code x}.
+ * Ermittelt den Arkussinus der angegebenen Zahl.
*
* @param x Eine Zahl.
* @return {@code asin(x)}.
@@ -707,7 +993,7 @@ public class Constants {
}
/**
- * Ermittelt den Arkuskosinus der Zahl {@code x}.
+ * Ermittelt den Arkuskosinus der angegebenen Zahl.
*
* @param x Eine Zahl.
* @return {@code acos(x)}.
@@ -717,7 +1003,7 @@ public class Constants {
}
/**
- * Ermittelt den Arkusktangens der Zahl {@code x}.
+ * Ermittelt den Arkusktangens der angegebenen Zahl.
*
* @param x Eine Zahl.
* @return {@code atan(x)}.
@@ -727,11 +1013,17 @@ public class Constants {
}
/**
- * Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
+ * Beschränkt die angegebene Zahl auf das Intervall {@code [min, max]}.
* Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
* zurückgegeben.
*
+ *
+ * double beschraenkt1 = limit(2.1, 0.0, 3.0); // 2.1
+ * double beschraenkt2 = limit(4.1, 0.0, 3.0); // 1.0
+ *
+ *
* @param x Eine Zahl.
+ * @param min Das Minimum des Intervalls.
* @param max Das Maximum des Intervalls.
* @return Eine Zahl im Intervall {@code [min, max]}.
*/
@@ -746,22 +1038,28 @@ public class Constants {
}
/**
- * Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
- * Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
+ * Beschränkt die angegebene Zahl auf das Intervall {@code [min, max]}.
+ * Liegt {@code i} außerhalb des Intervalls, wird eine der Grenzen
* zurückgegeben.
*
- * @param x Eine Zahl.
+ *
+ * double beschraenkt1 = limit(2, 0, 3); // 2
+ * double beschraenkt2 = limit(4, 0, 3); // 3
+ *
+ *
+ * @param i Eine Zahl.
+ * @param min Das Minimum des Intervalls.
* @param max Das Maximum des Intervalls.
* @return Eine Zahl im Intervall {@code [min, max]}.
*/
- public static final int limit( int x, int min, int max ) {
- if( x > max ) {
+ public static final int limit( int i, int min, int max ) {
+ if( i > max ) {
return max;
}
- if( x < min ) {
+ if( i < min ) {
return min;
}
- return x;
+ return i;
}
/**
@@ -771,30 +1069,91 @@ public class Constants {
* * from - t * (from + to) *+ *
+ * In der Regel liegt {@code t} im Intervall {@code [0, 1]}. Für + * {@code t = 0} ist das Ergebnis {@code from} und für {@code t = 1} ist das + * Ergebnis {@code to}. {@code t} kann aber auch Werte außerhalb des + * Intervalls annehmen. Für {@code t = 2} ist das Ergebnis beispielsweise + * {@code 2*to}; + * + *
+ * double interpoliert = interpolate(100.0, 500.0, 0.5); // 300.0
+ * double interpoliert = interpolate(100.0, 500.0, 1.0); // 500.0
+ * double interpoliert = interpolate(100.0, 500.0, 1.5); // 750.0
+ *
*
* @param from Startwert
* @param to Zielwert
- * @param t Wert zwischen 0 und 1.
+ * @param t Anteil des Ergebnisses auf der Strecke zwischen {@code from}
+ * und {@code to}.
* @return Das Ergebnis der linearen Interpolation.
*/
public static final double interpolate( double from, double to, double t ) {
return from + t * (to - from);
}
+ /**
+ * Interpoliert einen Wert zwischen {@code from} und {@code to}, aber
+ * beschränkt {@code t} auf das Intervall {@code [0, 1]}.
+ *
+ *
+ * double interpoliert = interpolate(100.0, 500.0, 0.5); // 300.0
+ * double interpoliert = interpolate(100.0, 500.0, 1.0); // 500.0
+ * double interpoliert = interpolate(100.0, 500.0, 1.5); // 500.0
+ *
+ *
+ * @param from Startwert
+ * @param to Zielwert
+ * @param t Wert zwischen 0 und 1.
+ * @return Das Ergebnis der linearen Interpolation.
+ * @see #interpolate(double, double, double)
+ */
public static final double morph( double from, double to, double t ) {
return interpolate(from, to, limit(t, 0.0, 1.0));
}
+ /**
+ * Bestimmt für den angegebenen Wert aus dem Intervall
+ * {@code [fromMin, fromMax]} einen zugehörigen Wert aus dem Intervall
+ * {@code [toMin, toMax]}.
+ * + * Für {@code value = fromMin} wird {@code toMin} zurückgegeben. Für + * {@code value = fromMax} ist das Ergebnis {@code toMax}. Befindet sich + * {@code value} genau mittig zwischen {@code fromMin} und {@code fromMax}, + * dann ist das Ergebnis genau {@code interpolate(toMin, toMax, 0.5)}. + *
+ * Mit {@code map()} lassen sich Werte eines Originalintervalls auf ein + * Zielintervalls transponieren. Dies ist beispielsweise hilfreich, um aus + * der Mausbewegung die relative Position auf der Zeichenfläche zu + * bestimmen: + * + *
+ * double relativeMouseX = map(mouseX, 0.0, canvasWidth, 0.0, 1.0);
+ *
+ *
+ * @param value Der Wert aus dem original Intervall.
+ * @param fromMin Untere Grenze des original Intervalls.
+ * @param fromMax Obere Grenze des original Intervalls.
+ * @param toMin Untere Grenze des Zielintervalls.
+ * @param toMax Obere Grenze des Zielintervalls.
+ * @return Der zugehörige Wert aus dem Zielintervall.
+ * @see #interpolate(double, double, double)
+ */
public static final double map( double value, double fromMin, double fromMax, double toMin, double toMax ) {
return interpolate(toMin, toMax, (value - fromMin) / (fromMax - fromMin));
}
/**
- * Shared Random instance
+ * Geteilte {@code Random}-Instanz für einheitliche Zufallszahlen.
*/
private static Random random = null;
- private static final Random getRandom() {
+ /**
+ * Gibt die geteilte {@code Random}-Instanz zurück.
+ *
+ * @return Die {@code Random}-Instanz.
+ */
+ private static Random getRandom() {
if( random == null ) {
random = new Random();
}
@@ -885,8 +1244,7 @@ public class Constants {
* Erzeugt einen zufälligen Wahrheitswert. {@code true} wird mit der
* Wahrscheinlichkeit {@code percent} Prozent erzeugt.
*
- * @param percent Eine Prozentzahl zwischen 0 und
- * 100.
+ * @param percent Eine Prozentzahl zwischen 0 und 100.
* @return Ein Wahrheitswert.
*/
public static final boolean randomBool( int percent ) {
@@ -897,6 +1255,7 @@ public class Constants {
* Erzeugt einen zufälligen Wahrheitswert. {@code true} wird mit der
* Wahrscheinlichkeit {@code weight} erzeugt.
*
+ * @param weight Wahrscheinlichkeit für {@code true}.
* @return Ein Wahrheitswert.
*/
public static final boolean randomBool( double weight ) {
@@ -917,6 +1276,7 @@ public class Constants {
* Wählt ein zufälliges Element aus dem Array aus.
*
* @param values Ein Array mit Werten, die zur Auswahl stehen.
+ * @param
+ * int bin = binary(10); // "1010"
+ *
+ *
+ * @param i Eine Zahl.
+ * @return Die binäre Darstellung der Zahl als Text.
+ */
+ public static final String binary( int i ) {
+ return Integer.toBinaryString(i);
+ }
+
+ /**
+ * Formt die angegebene Binärzahl in eine Dezimalzahl um.
+ *
+ *
+ * int dezimal = romBinary("1010"); // 10
+ *
+ *
+ * @param binary Ein Text nur aus "0" und "1".
+ * @return Der Wert der Binärzahl.
+ */
+ public static final int fromBinary( String binary ) {
+ return Integer.valueOf(binary, 2);
+ }
+
+ /**
+ * Formt die angegebene Zahl in ihre hexadezimal Darstellung um.
+ *
+ *
+ * int hexa = hex(255); // "FF"
+ *
+ *
+ * @param i Eine Zahl.
+ * @return Die hexadezimal Darstellung der Zahl als Text.
+ */
+ public static final String hex( int i ) {
+ return Integer.toHexString(i);
+ }
+
+ /**
+ * Formt die angegebene Binärzahl in eine Dezimalzahl um.
+ *
+ *
+ * int dezimal = romHex("FF"); // 255
+ *
+ *
+ * @param binary Ein Text nur aus den Zeichen "0" bis "9" und "A" bis "F".
+ * @return Der Wert der Binärzahl.
+ */
+ public static final int fromHex( String binary ) {
+ return Integer.valueOf(binary, 16);
+ }
+
+ // Konstants für Key events (Copied from KeyEvent)
/**
* Constant for the ENTER virtual key.
@@ -1248,7 +1781,7 @@ public class Constants {
*/
public static final int KEY_EQUALS = KeyEvent.VK_EQUALS;
- /** VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */
+ /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */
/**
* Constant for the "A" key.
diff --git a/src/main/java/schule/ngb/zm/Drawable.java b/src/main/java/schule/ngb/zm/Drawable.java
index 3511f73..0b93f88 100644
--- a/src/main/java/schule/ngb/zm/Drawable.java
+++ b/src/main/java/schule/ngb/zm/Drawable.java
@@ -12,7 +12,7 @@ public interface Drawable {
* Gibt an, ob das Objekt derzeit sichtbar ist (also gezeichnet werden
* muss).
*
- * @return true, wenn das Objekt sichtbar ist.
+ * @return {@code true}, wenn das Objekt sichtbar ist.
*/
boolean isVisible();
diff --git a/src/main/java/schule/ngb/zm/DrawingLayer.java b/src/main/java/schule/ngb/zm/DrawingLayer.java
index 4694527..4289aaf 100644
--- a/src/main/java/schule/ngb/zm/DrawingLayer.java
+++ b/src/main/java/schule/ngb/zm/DrawingLayer.java
@@ -191,7 +191,8 @@ public class DrawingLayer extends Layer {
}
public void pixel( double x, double y ) {
- square(x, y, 1);
+ // square(x, y, 1);
+ buffer.setRGB((int)x, (int)y, fillColor.getRGBA());
}
public void square( double x, double y, double w ) {
diff --git a/src/main/java/schule/ngb/zm/Options.java b/src/main/java/schule/ngb/zm/Options.java
index 70cf071..d903367 100644
--- a/src/main/java/schule/ngb/zm/Options.java
+++ b/src/main/java/schule/ngb/zm/Options.java
@@ -42,6 +42,7 @@ public final class Options {
public enum Direction {
CENTER(0, 0),
+
NORTH(0, -1),
EAST(1, 0),
SOUTH(0, 1),
@@ -49,14 +50,19 @@ public final class Options {
NORTHEAST(1, -1),
SOUTHEAST(1, 1),
- NORTHWEST(-1, -1),
SOUTHWEST(-1, 1),
+ NORTHWEST(-1, -1),
MIDDLE(CENTER),
UP(NORTH),
+ RIGHT(EAST),
DOWN(SOUTH),
LEFT(WEST),
- RIGHT(EAST);
+
+ UPLEFT(NORTHWEST),
+ DOWNLEFT(SOUTHWEST),
+ DOWNRIGHT(SOUTHEAST),
+ UPRIGHT(NORTHEAST);
public final byte x, y;
diff --git a/src/main/java/schule/ngb/zm/Updatable.java b/src/main/java/schule/ngb/zm/Updatable.java
index 0a60447..0998e1c 100644
--- a/src/main/java/schule/ngb/zm/Updatable.java
+++ b/src/main/java/schule/ngb/zm/Updatable.java
@@ -9,7 +9,7 @@ public interface Updatable {
/**
* Gibt an, ob das Objekt gerade auf Aktualisierungen reagiert.
- * @return true, wenn das Objekt aktiv ist.
+ * @return {@code true}, wenn das Objekt aktiv ist.
*/
public boolean isActive();
diff --git a/src/main/java/schule/ngb/zm/Zeichenleinwand.java b/src/main/java/schule/ngb/zm/Zeichenleinwand.java
index 73b9bae..d59fdaf 100644
--- a/src/main/java/schule/ngb/zm/Zeichenleinwand.java
+++ b/src/main/java/schule/ngb/zm/Zeichenleinwand.java
@@ -1,7 +1,5 @@
package schule.ngb.zm;
-import schule.ngb.zm.shapes.ShapesLayer;
-
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -37,12 +35,12 @@ public class Zeichenleinwand extends Canvas {
super.setSize(width, height);
this.setPreferredSize(this.getSize());
this.setMinimumSize(this.getSize());
- this.setBackground(Constants.STD_BACKGROUND.getJavaColor());
+ this.setBackground(Constants.DEFAULT_BACKGROUND.getJavaColor());
// Liste der Ebenen initialisieren und die Standardebenen einfügen
layers = new LinkedList<>();
synchronized( layers ) {
- layers.add(new ColorLayer(width, height, Constants.STD_BACKGROUND));
+ layers.add(new ColorLayer(width, height, Constants.DEFAULT_BACKGROUND));
}
}
@@ -90,7 +88,7 @@ public class Zeichenleinwand extends Canvas {
* als letzte eingefügt. Die aufrufende Methode kann also nicht sicher sein,
* dass die neue Ebene am Ende wirklich am Index {@code i} steht.
*
- * @param i Index der Ebene, beginnend mit 0.
+ * @param i Index der Ebene, beginnend mit 0.
* @param layer Die neue Ebene.
*/
public void addLayer( int i, Layer layer ) {
@@ -125,10 +123,10 @@ public class Zeichenleinwand extends Canvas {
}
/**
- * Holt die Ebene am Index i (beginnend bei 0).
+ * Holt die Ebene am Index i (beginnend bei 0).
*
- * @param i Index der Ebene (beginnend bei 0).
- * @return Die Ebene am Index i oder null.
+ * @param i Index der Ebene (beginnend bei 0).
+ * @return Die Ebene am Index i oder {@code null}.
* @throws IndexOutOfBoundsException Falls der Index nicht existiert.
*/
public Layer getLayer( int i ) {
@@ -141,7 +139,7 @@ public class Zeichenleinwand extends Canvas {
/**
* Sucht die erste Ebene des angegebenen Typs aus der Liste der Ebenen.
- * Existiert keine solche Ebene, wird null zurückgegeben.
+ * Existiert keine solche Ebene, wird {@code null} zurückgegeben.
*
* @param clazz Typ der Ebene.
* @param + * Die Lautstärke wird auf einer linearen Skale angegeben, wobei 0 kein Ton + * und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des + * Mediums. + * + * @return Die Lautstärke als linear skalierter Wert. + */ + double getVolume(); + /** * Startet die Wiedergabe des Mediums und beendet die Methode. Das * Audio-Medium wird einmal abgespielt und stoppt dann. */ - void playOnce(); + void play(); /** * Startet die Wiedergabe des Mediums und blockiert das Programm, bis die * Wiedergabe beendet ist. */ - void playOnceAndWait(); + void playAndWait(); /** * Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode diff --git a/src/main/java/schule/ngb/zm/media/Mixer.java b/src/main/java/schule/ngb/zm/media/Mixer.java index 91b2064..f94ec4f 100644 --- a/src/main/java/schule/ngb/zm/media/Mixer.java +++ b/src/main/java/schule/ngb/zm/media/Mixer.java @@ -50,30 +50,53 @@ public class Mixer implements Audio { pAudio.setVolume(pVolumeFactor * volume); } + /** + * {@inheritDoc} + */ @Override public boolean isPlaying() { return audios.stream().anyMatch(aw -> aw.audio.isPlaying()); } + /** + * {@inheritDoc} + */ @Override public boolean isLooping() { return audios.stream().anyMatch(aw -> aw.audio.isLooping()); } + /** + * {@inheritDoc} + */ @Override public void setVolume( double pVolume ) { volume = (float) pVolume; audios.stream().forEach(aw -> aw.audio.setVolume(aw.volumeFactor * pVolume)); } + /** + * {@inheritDoc} + */ @Override - public void playOnce() { - audios.stream().forEach(aw -> aw.audio.playOnce()); + public double getVolume() { + return volume; } + /** + * {@inheritDoc} + */ @Override - public void playOnceAndWait() { - audios.stream().forEach(aw -> aw.audio.playOnce()); + public void play() { + audios.stream().forEach(aw -> aw.audio.play()); + } + + /** + * {@inheritDoc} + */ + @Override + public void playAndWait() { + audios.stream().forEach(aw -> aw.audio.play()); while( audios.stream().anyMatch(aw -> aw.audio.isPlaying()) ) { try { Thread.sleep(10); @@ -83,16 +106,25 @@ public class Mixer implements Audio { } } + /** + * {@inheritDoc} + */ @Override public void loop() { audios.stream().forEach(aw -> aw.audio.loop()); } + /** + * {@inheritDoc} + */ @Override public void stop() { audios.stream().forEach(aw -> aw.audio.stop()); } + /** + * {@inheritDoc} + */ @Override public void dispose() { if( isPlaying() ) { @@ -101,6 +133,18 @@ public class Mixer implements Audio { audios.stream().forEach(aw -> aw.audio.dispose()); } + /** + * Ändert die Lautstärke aller hinzugefügten Audiomedien in der angegebenen + * Zeit schrittweise, bis die angegebene Lautstärke erreicht ist. + *
+ * Zu beachten ist, dass die Lautstärke des Mixers angepasst wird. Das + * bedeutet, dass die Lautstärke der hinzugefügten Medien mit ihrem + * Lautstärkefaktor multipliziert werden. Die Medien haben am Ende also + * nicht unbedingt die Lautstärke {@code to}. + * + * @param to Der Zielwert für die Lautstärke. + * @param time Die Zeit, nach der die Änderung abgeschlossen sein soll. + */ public void fade( final double to, final int time ) { TaskRunner.run(new Runnable() { @Override @@ -108,9 +152,6 @@ public class Mixer implements Audio { final long start = System.currentTimeMillis(); double t = 0.0; double from = volume; - if( !isPlaying() ) { - playOnce(); - } do { setVolume(Constants.interpolate(from, to, t)); t = (double) (System.currentTimeMillis() - start) / (double) time; diff --git a/src/main/java/schule/ngb/zm/media/Music.java b/src/main/java/schule/ngb/zm/media/Music.java index fa03cf3..72ad34c 100644 --- a/src/main/java/schule/ngb/zm/media/Music.java +++ b/src/main/java/schule/ngb/zm/media/Music.java @@ -1,43 +1,82 @@ package schule.ngb.zm.media; import schule.ngb.zm.tasks.TaskRunner; +import schule.ngb.zm.util.Log; import schule.ngb.zm.util.ResourceStreamProvider; +import schule.ngb.zm.util.Validator; import javax.sound.sampled.*; -import java.io.BufferedInputStream; import java.io.IOException; -import java.io.InputStream; import java.net.URL; -import java.util.logging.Level; -import java.util.logging.Logger; +/** + * Ein Musikstück, dass im Projekt abgespielt werden soll. + *
+ * Im gegensatz zu einem {@link Sound} sind Musikstücke längere Audiodateien, + * die zum Beispiel als Hintergrundmusik ablaufen sollen. Die Musik wird daher + * nicht komplett in den Speicher geladen, sondern direkt aus der Audioquelle + * gestreamt und wiedergegeben. + */ public class Music implements Audio { // size of the byte buffer used to read/write the audio stream private static final int BUFFER_SIZE = 4096; + /** + * Ob der Sound gerade abgespielt wird. + */ private boolean playing = false; + /** + * Ob der Sound gerade in einer Schleife abgespielt wird. + */ private boolean looping = false; + /** + * Die Quelle des Musikstücks. + */ private String audioSource; + /** + * Der AudioStream, um die AUdiosdaten zulsen, falls dieser schon geöffnet + * wurde. Sonst {@code null}. + */ private AudioInputStream audioStream; + /** + * Die Line für die Ausgabe, falls diese schon geöffnet wurde. Sonst + * {@code null}. + */ private SourceDataLine audioLine; + /** + * Die Lautstärke der Musik. + */ private float volume = 0.8f; + /** + * Erstellt eine Musik aus der angegebenen Datei oder Webadresse. + * + * @param source Ein Dateipfad oder eine Webadresse. + * @throws NullPointerException Falls die Quelle {@code null} ist. + */ public Music( String source ) { + Validator.requireNotNull(source); this.audioSource = source; } + /** + * {@inheritDoc} + */ @Override public boolean isPlaying() { return playing; } + /** + * {@inheritDoc} + */ @Override public boolean isLooping() { if( !playing ) { @@ -46,6 +85,9 @@ public class Music implements Audio { return looping; } + /** + * {@inheritDoc} + */ @Override public void setVolume( double volume ) { this.volume = (float) volume; @@ -54,6 +96,18 @@ public class Music implements Audio { } } + /** + * {@inheritDoc} + */ + @Override + public double getVolume() { + return volume; + } + + /** + * Interne Methode, um die gesetzte Lautstärke vor dem Abspielen + * anzuwenden. + */ private void applyVolume() { FloatControl gainControl = (FloatControl) audioLine.getControl(FloatControl.Type.MASTER_GAIN); @@ -63,8 +117,11 @@ public class Music implements Audio { gainControl.setValue(vol); } + /** + * {@inheritDoc} + */ @Override - public void playOnce() { + public void play() { if( openLine() ) { TaskRunner.run(new Runnable() { @Override @@ -75,19 +132,28 @@ public class Music implements Audio { } } + /** + * {@inheritDoc} + */ @Override - public void playOnceAndWait() { + public void playAndWait() { if( openLine() ) { stream(); } } + /** + * {@inheritDoc} + */ @Override public void loop() { looping = true; - playOnce(); + play(); } + /** + * {@inheritDoc} + */ @Override public void stop() { playing = false; @@ -95,6 +161,9 @@ public class Music implements Audio { dispose(); } + /** + * {@inheritDoc} + */ @Override public void dispose() { if( audioLine != null ) { @@ -112,7 +181,8 @@ public class Music implements Audio { if( audioStream != null ) { audioStream.close(); } - } catch( IOException ex ) {} + } catch( IOException ex ) { + } audioLine = null; audioStream = null; @@ -126,15 +196,14 @@ public class Music implements Audio { int bytesRead = -1; try { - while (playing && (bytesRead = audioStream.read(bytesBuffer)) != -1) { + while( playing && (bytesRead = audioStream.read(bytesBuffer)) != -1 ) { audioLine.write(bytesBuffer, 0, bytesRead); } audioLine.drain(); audioLine.stop(); } catch( IOException ex ) { - LOGGER.warning("Error while playing Music source <" + audioSource + ">"); - LOGGER.throwing("Music", "stream", ex); + LOG.warn(ex, "Error while playing Music source <%s>", audioSource); } // Wait for the remaining audio to play @@ -163,7 +232,7 @@ public class Music implements Audio { final int ch = format.getChannels(); final float rate = format.getSampleRate(); - AudioFormat outFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch*2, rate, false); + AudioFormat outFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch * 2, rate, false); DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat); @@ -174,14 +243,14 @@ public class Music implements Audio { audioStream = AudioSystem.getAudioInputStream(outFormat, inStream); return true; } else { - LOGGER.warning("Sound source <" + audioSource + "> could not be played: No audio source found."); + LOG.warn("Sound source <%s> could not be played: No audio source found.", audioSource); } } catch( UnsupportedAudioFileException ex ) { - LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: The specified audio file is not supported.", ex); + LOG.warn(ex, "Sound source <%s> could not be played: The specified audio file is not supported.", audioSource); } catch( LineUnavailableException ex ) { - LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Audio line for playing back is unavailable.", ex); + LOG.warn(ex, "Sound source <%s> could not be played: Audio line for playing back is unavailable.", audioSource); } catch( IOException ex ) { - LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Error playing the audio file.", ex); + LOG.warn(ex, "Sound source <%s> could not be played: Error playing the audio file.", audioSource); } return false; } @@ -201,7 +270,6 @@ public class Music implements Audio { } } - //private static final Logger LOGGER = Logger.getLogger("schule.ngb.zm.media.Music"); - private static final Logger LOGGER = Logger.getLogger(Music.class.getName()); + private static final Log LOG = Log.getLogger(Music.class); } diff --git a/src/main/java/schule/ngb/zm/media/Sound.java b/src/main/java/schule/ngb/zm/media/Sound.java index ceda917..ece650d 100644 --- a/src/main/java/schule/ngb/zm/media/Sound.java +++ b/src/main/java/schule/ngb/zm/media/Sound.java @@ -1,35 +1,84 @@ package schule.ngb.zm.media; +import schule.ngb.zm.util.Log; import schule.ngb.zm.util.ResourceStreamProvider; +import schule.ngb.zm.util.Validator; import javax.sound.sampled.*; import java.io.IOException; import java.io.InputStream; import java.util.logging.Logger; +/** + * Wiedergabe kurzer Soundclips, die mehrmals wiederverwendet werden. + *
+ * In Spielen und anderen Projekten gibt es oftmals eine Reihe kurzer Sounds, + * die zusammen mit bestimmten Aktionen wiedergegeben werden (zum Beispiel, wenn + * die Spielfigur springt, wenn zwei Objekte kollidieren, usw.). Sounds werden + * komplett in den Speicher geladen und können dadurch immer wieder, als + * Schleife oder auch nur Abschnittsweise abgespielt werden. + *
+ * Für längre Musikstücke (beispielsweise Hintergrundmusik) bietet sich eher die + * KLasse {@link Music} an. + */ +@SuppressWarnings( "unused" ) public class Sound implements Audio { + /** + * Ob der Sound gerade abgespielt wird. + */ private boolean playing = false; + /** + * Ob der Sound gerade in einer Schleife abgespielt wird. + */ private boolean looping = false; + /** + * Die Quelle des Musikstücks. + */ private String audioSource; + /** + * Der Clip, falls er schon geladen wurde, sonst {@code null}. + */ private Clip audioClip; + /** + * Ob die Resourcen des Clips im Speicher nach dem nächsten Abspielen + * freigegeben werden sollen. + */ private boolean disposeAfterPlay = false; + /** + * Die Lautstärke des Clips. + */ private float volume = 0.8f; + /** + * Erstellt einen Sound aus der angegebene Quelle. + * + * @param source Ein Dateipfad oder eine Webadresse. + * @throws NullPointerException Falls die Quelle {@code null} ist. + */ public Sound( String source ) { + Validator.requireNotNull(source); this.audioSource = source; } + /** + * {@inheritDoc} + */ + @Override public boolean isPlaying() { // return audioClip != null && audioClip.isRunning(); return playing; } + /** + * {@inheritDoc} + */ + @Override public boolean isLooping() { if( !playing ) { looping = false; @@ -37,6 +86,10 @@ public class Sound implements Audio { return looping; } + /** + * {@inheritDoc} + */ + @Override public void setVolume( double volume ) { this.volume = (float) volume; if( audioClip != null ) { @@ -44,6 +97,18 @@ public class Sound implements Audio { } } + /** + * {@inheritDoc} + */ + @Override + public double getVolume() { + return volume; + } + + /** + * Interne Methode, um die gesetzte Lautstärke vor dem Abspielen + * anzuwenden. + */ private void applyVolume() { FloatControl gainControl = (FloatControl) audioClip.getControl(FloatControl.Type.MASTER_GAIN); @@ -53,6 +118,10 @@ public class Sound implements Audio { gainControl.setValue(vol); } + /** + * {@inheritDoc} + */ + @Override public void stop() { looping = false; if( audioClip.isRunning() ) { @@ -61,6 +130,10 @@ public class Sound implements Audio { playing = false; } + /** + * {@inheritDoc} + */ + @Override public void play() { if( this.openClip() ) { audioClip.start(); @@ -68,16 +141,10 @@ public class Sound implements Audio { } } - public void playOnce() { - disposeAfterPlay = true; - play(); - } - - public void playOnceAndWait() { - disposeAfterPlay = true; - playAndWait(); - } - + /** + * {@inheritDoc} + */ + @Override public void playAndWait() { this.play(); @@ -94,26 +161,73 @@ public class Sound implements Audio { audioClip.close(); } + /** + * Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips + * frei. + *
+ * Der Aufruf ist effektiv gleich zu + *
+ * clip.playAndWait();
+ * clip.dispose();
+ *
+ * allerdings wird der aufrufende Thread nicht blockiert und
+ * {@link #dispose()} automatisch am Ende aufgerufen.
+ */
+ public void playOnce() {
+ disposeAfterPlay = true;
+ play();
+ }
+
+ /**
+ * Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
+ * frei.
+ * + * Der Aufruf entspricht + *
+ * clip.playAndWait();
+ * clip.dispose();
+ *
+ */
+ public void playOnceAndWait() {
+ disposeAfterPlay = true;
+ playAndWait();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void loop() {
loop(Clip.LOOP_CONTINUOUSLY);
}
+ /**
+ * Wiederholt den Sound die angegebene Anzahl an Wiederholungen ab und stoppt
+ * die Wiedergabe dann.
+ * @param count Anzahl der Wiederholungen.
+ */
public void loop( int count ) {
- int loopCount = count;
- if( loopCount != Clip.LOOP_CONTINUOUSLY ) {
- if( loopCount <= 0 ) {
- return;
+ if( count > 0 ) {
+ int loopCount = count;
+ if( loopCount != Clip.LOOP_CONTINUOUSLY ) {
+ if( loopCount <= 0 ) {
+ return;
+ }
+ // Adjust Number of loops
+ loopCount -= 1;
+ }
+ if( openClip() ) {
+ looping = true;
+ audioClip.loop(loopCount);
+ playing = true;
}
- // Adjust Number of loops
- loopCount -= 1;
- }
- if( openClip() ) {
- looping = true;
- audioClip.loop(loopCount);
- playing = true;
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void dispose() {
if( audioClip != null ) {
if( audioClip.isRunning() ) {
@@ -150,17 +264,14 @@ public class Sound implements Audio {
applyVolume();
return true;
} else {
- LOGGER.warning("Sound source " + audioSource + " could not be played: No audio source found.");
+ LOG.warn("Sound source <%s> could not be played: No audio source found.", audioSource);
}
} catch( UnsupportedAudioFileException ex ) {
- LOGGER.warning("Sound source " + audioSource + " could not be played: The specified audio file is not supported.");
- LOGGER.throwing("Sound", "openClip", ex);
+ LOG.warn(ex, "Sound source <%s> could not be played: The specified audio file is not supported.", audioSource);
} catch( LineUnavailableException ex ) {
- LOGGER.warning("Sound source " + audioSource + " could not be played: Audio line for playing back is unavailable.");
- LOGGER.throwing("Sound", "openClip", ex);
+ LOG.warn(ex, "Sound source <%s> could not be played: Audio line for playing back is unavailable.", audioSource);
} catch( IOException ex ) {
- LOGGER.warning("Sound source " + audioSource + " could not be played: Error playing the audio file.");
- LOGGER.throwing("Sound", "openClip", ex);
+ LOG.warn(ex, "Sound source <%s> could not be played: Error playing the audio file.", audioSource);
}
return false;
}
@@ -180,6 +291,11 @@ public class Sound implements Audio {
}
}*/
+ /**
+ * Interne Methode, die aufgerufen wird, wenn die Wiedergabe gestoppt wird.
+ * Entweder durch einen Aufruf von {@link #stop()} oder, weil die Wiedergabe
+ * nach {@link #playOnce()} beendet wurde.
+ */
private void playbackStopped() {
playing = false;
if( disposeAfterPlay ) {
@@ -188,17 +304,6 @@ public class Sound implements Audio {
}
}
- /*
- public void addLineListener( LineListener listener ) {
- if( audioClip == null ) {
- openClip();
- }
- if( audioClip != null ) {
- audioClip.addLineListener(listener);
- }
- }
- */
-
- private static final Logger LOGGER = Logger.getLogger(Sound.class.getName());
+ private static final Log LOG = Log.getLogger(Sound.class);
}
diff --git a/src/main/java/schule/ngb/zm/shapes/Shape.java b/src/main/java/schule/ngb/zm/shapes/Shape.java
index 119e107..d8219f3 100644
--- a/src/main/java/schule/ngb/zm/shapes/Shape.java
+++ b/src/main/java/schule/ngb/zm/shapes/Shape.java
@@ -168,8 +168,7 @@ public abstract class Shape extends FilledShape {
*
* Unterklassen sollten diese Methode überschreiben, um weitere
* Eigenschaften zu kopieren (zum Beispiel den Radius eines Kreises). Mit
- * dem Aufruf
- * super.copyFrom(shape) sollten die Basiseigenschaften
+ * dem Aufruf {@code super.copyFrom(shape)} sollten die Basiseigenschaften
* kopiert werden.
*
* @param shape
@@ -243,7 +242,7 @@ public abstract class Shape extends FilledShape {
}
public void alignTo( Options.Direction dir, double buff ) {
- Point2D anchorShape = Shape.getAnchorPoint(width, height, dir);
+ Point2D anchorShape = Shape.getAnchorPoint(canvasWidth, canvasHeight, dir);
Point2D anchorThis = this.getAbsAnchorPoint(dir);
this.x += Math.abs(dir.x) * (anchorShape.getX() - anchorThis.getX()) + dir.x * buff;
diff --git a/src/main/java/schule/ngb/zm/shapes/ShapeGroup.java b/src/main/java/schule/ngb/zm/shapes/ShapeGroup.java
index 46a56d5..b8548da 100644
--- a/src/main/java/schule/ngb/zm/shapes/ShapeGroup.java
+++ b/src/main/java/schule/ngb/zm/shapes/ShapeGroup.java
@@ -5,6 +5,7 @@ import schule.ngb.zm.Options;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -28,9 +29,20 @@ import java.util.List;
* benötigen, erst nach Hinzufügen der Gruppenelemente ausgeführt werden.
* Nachdem sich die Zusammensetzung der Gruppe geändert hat, muss die Gruppe
* ggf. neu ausgerichtet werden.
+ *
+ * Für die Ausrichtung der Elemente in einer Gruppe können
+ * {@link #arrange(Options.Direction, double)},
+ * {@link #arrangeInGrid(int, Options.Direction, double, int)} und
+ * {@link #align(Options.Direction)} verwendet werden, die jeweils die Position
+ * der Formen in der Gruppe ändern und nicht die Position der Gruppe selbst (so
+ * wie z.B. {@link #alignTo(Shape, Options.Direction)}.
*/
public class ShapeGroup extends Shape {
+ public static final int ARRANGE_ROWS = 0;
+
+ public static final int ARRANGE_COLS = 1;
+
private List
+ * Die Implementierung basiert auf dem von Ken Perlin entwickelten Algorithmus
+ * und wurde anhand der Beschreibung von
+ * FLAFLA2 implementiert.
+ */
+public class Noise {
+
+ private static final int N = 256;
+
+ private static final int M = N - 1;
+
+ /**
+ * Interne Permutationstabelle für diesen Generator
+ */
+ private int[] p;
+
+ private double octaves = 1, persistence = .5;
+
+ private double frequency = 1;
+
+ private double amplitude = 1;
+
+ private int repeat = -1;
+
+ private double rangeMin = 0.0, rangeMax = 1.0;
+
+ public Noise() {
+ this(null);
+ }
+
+ public Noise( long seed ) {
+ init(new Random(seed));
+ }
+
+ /**
+ * Initialisiert diesen Perlin-Noise mit dem angegebenen Zufallsgenerator.
+ *
+ * @param rand
+ */
+ public Noise( Random rand ) {
+ init(rand);
+ }
+
+ public double getOctaves() {
+ return octaves;
+ }
+
+ public void setOctaves( double pOctaves ) {
+ this.octaves = pOctaves;
+ }
+
+ public double getPersistence() {
+ return persistence;
+ }
+
+ public void setPersistence( double pPersistence ) {
+ this.persistence = pPersistence;
+ }
+
+ public double getFrequency() {
+ return frequency;
+ }
+
+ public void setFrequency( double pFrequency ) {
+ this.frequency = pFrequency;
+ }
+
+ public double getAmplitude() {
+ return amplitude;
+ }
+
+ public void setAmplitude( double pAmplitude ) {
+ this.amplitude = pAmplitude;
+ }
+
+ public void setRange( double pRangeMin, double pRangeMax ) {
+ this.rangeMin = pRangeMin;
+ this.rangeMax = pRangeMax;
+ }
+
+ public double getRangeMin() {
+ return rangeMin;
+ }
+
+ public double getRangeMax() {
+ return rangeMax;
+ }
+
+ public int getRepeat() {
+ return repeat;
+ }
+
+ public void setRepeat( int pRepeat ) {
+ this.repeat = pRepeat;
+ }
+
+ public double noise( double x ) {
+ double total = 0;
+ double freq = this.frequency;
+ double amp = this.amplitude;
+ double maxValue = 0;
+
+ for( int i = 0; i < octaves; i++ ) {
+ total += perlin(x * freq) * amp;
+
+ maxValue += amp;
+
+ amp *= persistence;
+ freq *= 2;
+ }
+
+ return lerp(rangeMin, rangeMax, (total / maxValue));
+ }
+
+ public double noise( double x, double y ) {
+ double total = 0;
+ double freq = this.frequency;
+ double amp = this.amplitude;
+ double maxValue = 0;
+
+ for( int i = 0; i < octaves; i++ ) {
+ total += perlin(x * freq, y * freq) * amp;
+
+ maxValue += amp;
+
+ amp *= persistence;
+ freq *= 2;
+ }
+
+ return lerp(rangeMin, rangeMax, (total / maxValue));
+ }
+
+ public double noise( double x, double y, double z ) {
+ double total = 0;
+ double freq = this.frequency;
+ double amp = this.amplitude;
+ double maxValue = 0;
+
+ for( int i = 0; i < octaves; i++ ) {
+ total += perlin(x * freq, y * freq, z * freq) * amp;
+
+ maxValue += amp;
+
+ amp *= persistence;
+ freq *= 2;
+ }
+
+ return lerp(rangeMin, rangeMax, (total / maxValue));
+ }
+
+ private double perlin( double x ) {
+ // @formatter:off
+ if( repeat > 0 ) {
+ x %= repeat;
+ }
+
+ int xi = (int)x & M;
+
+ double xf = x - (int)x;
+
+ double u = fade(xf);
+
+ int a, b;
+ a = p[ xi ];
+ b = p[inc(xi)];
+
+ return (lerp(grad(a,xf), grad(b,xf-1), u) + 1) / 2;
+ // @formatter:on
+ }
+
+ private double perlin( double x, double y ) {
+ // @formatter:off
+ if( repeat > 0 ) {
+ x %= repeat;
+ y %= repeat;
+ }
+
+ int xi = (int) x & M;
+ int yi = (int) y & M;
+
+ double xf = x - (int) x;
+ double yf = y - (int) y;
+
+ double u = fade(xf);
+ double v = fade(yf);
+
+ int aa, ab, ba, bb;
+ aa = p[p[ xi ] + yi ];
+ ab = p[p[ xi ] + inc(yi)];
+ ba = p[p[inc(xi)] + yi ];
+ bb = p[p[inc(xi)] + inc(yi)];
+
+ double x1, x2;
+ x1 = lerp(
+ grad(aa, xf , yf),
+ grad(ba, xf-1, yf),
+ u);
+ x2 = lerp(
+ grad(ab, xf , yf-1),
+ grad(bb, xf-1, yf-1),
+ u);
+
+ return (lerp(x1, x2, v) + 1) / 2;
+ // @formatter:on
+ }
+
+ private double perlin( double x, double y, double z ) {
+ // @formatter:off
+ if( repeat > 0 ) {
+ x %= repeat;
+ y %= repeat;
+ z %= repeat;
+ }
+
+ int xi = (int)x & M;
+ int yi = (int)y & M;
+ int zi = (int)z & M;
+ double xf = x - (int)x;
+ double yf = y - (int)y;
+ double zf = z - (int)z;
+
+ double u = fade(xf);
+ double v = fade(yf);
+ double w = fade(zf);
+
+ int aaa, aba, aab, abb, baa, bba, bab, bbb;
+ aaa = p[p[p[ xi ] + yi ] + zi ];
+ aba = p[p[p[ xi ] + inc(yi)] + zi ];
+ aab = p[p[p[ xi ] + yi ] + inc(zi)];
+ abb = p[p[p[ xi ] + inc(yi)] + inc(zi)];
+ baa = p[p[p[inc(xi)] + yi ] + zi ];
+ bba = p[p[p[inc(xi)] + inc(yi)] + zi ];
+ bab = p[p[p[inc(xi)] + yi ] + inc(zi)];
+ bbb = p[p[p[inc(xi)] + inc(yi)] + inc(zi)];
+
+ double x1, x2, y1, y2;
+ x1 = lerp(
+ grad(aaa, xf , yf, zf),
+ grad(baa, xf-1, yf, zf),
+ u);
+ x2 = lerp(
+ grad(aba, xf , yf-1, zf),
+ grad(bba, xf-1, yf-1, zf),
+ u);
+ y1 = lerp(x1, x2, v);
+
+ x1 = lerp(
+ grad(aab, xf , yf, zf-1),
+ grad(bab, xf-1, yf, zf-1),
+ u);
+ x2 = lerp(
+ grad(abb, xf , yf-1, zf-1),
+ grad(bbb, xf-1, yf-1, zf-1),
+ u);
+ y2 = lerp(x1, x2, v);
+
+ return (lerp(y1, y2, w) + 1) / 2;
+ // @formatter:on
+ }
+
+ public void init( Random rand ) {
+ p = new int[N * 2];
+
+ if( rand == null ) {
+ System.arraycopy(PERLIN_PERMUTATION, 0, p, 0, N);
+ } else {
+ // Generate random permutation
+ for( int i = 0; i < N; i++ ) {
+ int n = rand.nextInt(N);
+ if( p[n] == 0 )
+ p[n] = i;
+ else
+ i--;
+ }
+ }
+
+ // Duplicate permutation array to prevent overflow errors
+ System.arraycopy(p, 0, p, N, N);
+ }
+
+ private double fade( double t ) {
+ return t * t * t * (t * (t * 6 - 15) + 10);
+ }
+
+ private double lerp( double a, double b, double t ) {
+ return a + t * (b - a);
+ }
+
+ private int inc( int i ) {
+ ++i;
+ if( repeat > 0 )
+ i = i % repeat;
+ return i;
+ }
+
+ private double grad( int hash, double x ) {
+ switch( hash & 0x1 ) {
+ // @formatter:off
+ case 0x0: return x;
+ case 0x1: return -x;
+ default: return 0;
+ // @formatter:on
+ }
+ }
+
+
+ private double grad( int hash, double x, double y ) {
+ switch( hash & 0x3 ) {
+ // @formatter:off
+ case 0x0: return x;
+ case 0x1: return -x;
+ case 0x2: return y;
+ case 0x3: return -y;
+ default: return 0;
+ // @formatter:on
+ }
+ }
+
+ private double grad( int hash, double x, double y, double z ) {
+ switch( hash & 0xF ) {
+ // @formatter:off
+ case 0x0: return x + y;
+ case 0x1: return -x + y;
+ case 0x2: return x - y;
+ case 0x3: return -x - y;
+ case 0x4: return x + z;
+ case 0x5: return -x + z;
+ case 0x6: return x - z;
+ case 0x7: return -x - z;
+ case 0x8: return y + z;
+ case 0x9: return -y + z;
+ case 0xA: return y - z;
+ case 0xB: return -y - z;
+ case 0xC: return y + x;
+ case 0xD: return -y + z;
+ case 0xE: return y - x;
+ case 0xF: return -y - z;
+ default: return 0; // never happens
+ // @formatter:on
+ }
+ }
+
+ private static final int[] PERLIN_PERMUTATION = new int[]{
+ 151, 160, 137, 91, 90, 15,
+ 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
+ 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
+ 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
+ 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
+ 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
+ 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
+ 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
+ 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
+ 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
+ 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
+ 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
+ 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
+ };
+
+}
diff --git a/src/main/java/schule/ngb/zm/util/Validator.java b/src/main/java/schule/ngb/zm/util/Validator.java
index 50067b9..cb28143 100644
--- a/src/main/java/schule/ngb/zm/util/Validator.java
+++ b/src/main/java/schule/ngb/zm/util/Validator.java
@@ -136,8 +136,27 @@ public class Validator {
}
*/
+
+ public static final null, wenn das Bild nicht geladen werden
+ * Bild zurück oder {@code null}, wenn das Bild nicht geladen werden
* konnte. Ist ein Bild mit der angegebenen Quelle im Cache, wird das
* gespeicherte Bild zurückgegeben. Dies kann mit {@code caching = false}
* verhindert werden.
diff --git a/src/main/java/schule/ngb/zm/util/Noise.java b/src/main/java/schule/ngb/zm/util/Noise.java
new file mode 100644
index 0000000..e638f49
--- /dev/null
+++ b/src/main/java/schule/ngb/zm/util/Noise.java
@@ -0,0 +1,366 @@
+package schule.ngb.zm.util;
+
+import java.util.Random;
+
+/**
+ * Zufallsgenerator für Perlin-Noise.
+ *