Merge branch 'main' into events

This commit is contained in:
ngb
2022-07-15 21:40:55 +02:00
22 changed files with 1718 additions and 264 deletions

View File

@@ -3,17 +3,15 @@ package schule.ngb.zm;
/** /**
* Repräsentiert eine Farbe in der Zeichenmaschine. * Repräsentiert eine Farbe in der Zeichenmaschine.
* <p> * <p>
* Farben bestehen entweder aus einem Grauwert (zwischen 0 und * Farben bestehen entweder aus einem Grauwert (zwischen 0 und 255) oder einem
* 255) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen * Rot-, Grün- und Blauanteil (jeweils zwischen 0 und 255).
* 0 und 255).
* <p> * <p>
* Eine Farbe hat außerdem einen Transparenzwert zwischen 0 * Eine Farbe hat außerdem einen Transparenzwert zwischen 0 (unsichtbar) und 255
* (unsichtbar) und 255 (deckend). * (deckend).
*/ */
public class Color { public class Color {
//@formatter:off //@formatter:off
/** /**
* Die Farbe Schwarz (Grauwert 0). * Die Farbe Schwarz (Grauwert 0).
@@ -124,8 +122,8 @@ public class Color {
/** /**
* Erstellt eine Farbe. Die Parameter <var>red</var>, <var>green</var> und * Erstellt eine Farbe. Die Parameter <var>red</var>, <var>green</var> und
* <var>blue</var> geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte * <var>blue</var> geben die Rot-, Grün- und Blauanteile der Farbe. Die
* liegen zwischen 0 und 255. * 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 green Grünwert zwischen 0 und 255.
@@ -137,12 +135,10 @@ public class Color {
/** /**
* Erstellt eine Farbe. Die Parameter <var>red</var>, <var>green</var> und * Erstellt eine Farbe. Die Parameter <var>red</var>, <var>green</var> und
* <var>blue</var> geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte * <var>blue</var> geben die Rot-, Grün- und Blauanteile der Farbe. Die
* liegen zwischen 0 und 255. * Werte liegen zwischen 0 und 255.
* <var>alpha</var> gibt den den Transparentwert an (auch zwischen * <var>alpha</var> gibt den den Transparentwert an (auch zwischen
* 0 und 255), wobei * 0 und 255), wobei 0 komplett durchsichtig ist und 255 komplett deckend.
* 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 green Grünwert zwischen 0 und 255.
@@ -150,7 +146,7 @@ public class Color {
* @param alpha Transparentwert zwischen 0 und 255. * @param alpha Transparentwert zwischen 0 und 255.
*/ */
public Color( int red, int green, int blue, int alpha ) { 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 * Interner Konstruktor für die Initialisierung einer Farbe mit einem
* RGBA-Wert. * RGBA-Wert.
* * <p>
* Da der Konstruktor {@link #Color(int)} schon besetzt ist, muss hier der * Da der Konstruktor {@link #Color(int)} schon besetzt ist, muss hier der
* Parameter {@code isRGBA} auf {@code true} gesetzt werden, damit {@code rgba} * Parameter {@code isRGBA} auf {@code true} gesetzt werden, damit
* korrekt interpretiert wird. * {@code rgba} korrekt interpretiert wird.
*
* @param rgba RGBA-wert der Farbe. * @param rgba RGBA-wert der Farbe.
* @param isRGBA Sollte immer {@code true} sein. * @param isRGBA Sollte immer {@code true} sein.
*/ */
@@ -235,9 +232,9 @@ public class Color {
} }
/** /**
* Erzeugt eine Farbe aus einem hexadezimalen Code. Der Hexcode kann * Erzeugt eine Farbe aus einem hexadezimalen Code. Der Hexcode kann sechs-
* sechs- oder achtstellig sein (wenn ein Transparentwert vorhanden ist). * oder achtstellig sein (wenn ein Transparentwert vorhanden ist). Dem Code
* Dem Code kann ein {@code #} Zeichen vorangestellt sein. * kann ein {@code #} Zeichen vorangestellt sein.
* *
* @param hexcode * @param hexcode
* @return * @return
@@ -336,10 +333,11 @@ public class Color {
/** /**
* Konvertiert eine Farbe mit Komponenten im HSL-Farbraum in den * Konvertiert eine Farbe mit Komponenten im HSL-Farbraum in den
* RGB-Farbraum. * RGB-Farbraum.
* * <p>
* Die Farbkomponenten werden als Float-Array übergeben. Im Index 0 steht * 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 * der h-Wert im Bereich 0 bis 360, Index 1 und 2 enthalten den s- und
* l-Wert im Bereich von 0 bis 1. * l-Wert im Bereich von 0 bis 1.
*
* @param hsl Die Farbkomponenten im HSL-Farbraum. * @param hsl Die Farbkomponenten im HSL-Farbraum.
* @param alpha Ein Transparenzwert im Bereich 0 bis 255. * @param alpha Ein Transparenzwert im Bereich 0 bis 255.
* @return Der RGBA-Wert der Farbe. * @return Der RGBA-Wert der Farbe.
@@ -392,6 +390,7 @@ public class Color {
/** /**
* Erzeugt eine Kopie dieser Farbe. * Erzeugt eine Kopie dieser Farbe.
*
* @return Ein neues Farbobjekt. * @return Ein neues Farbobjekt.
*/ */
public Color copy() { public Color copy() {
@@ -400,10 +399,11 @@ public class Color {
/** /**
* Gibt den RGBA-Wert dieser Farbe zurück. * Gibt den RGBA-Wert dieser Farbe zurück.
* * <p>
* Eine Farbe wird als 32-Bit Integer gespeichert. Bits 24-31 enthalten * Eine Farbe wird als 32-Bit Integer gespeichert. Bits 24-31 enthalten den
* den Transparenzwert, 16-23 den Rotwert, 8-15 den Grünwert und 0-7 den * Transparenzwert, 16-23 den Rotwert, 8-15 den Grünwert und 0-7 den
* Blauwert der Farbe. * Blauwert der Farbe.
*
* @return Der RGBA-Wert der Farbe. * @return Der RGBA-Wert der Farbe.
* @see #getRed() * @see #getRed()
* @see #getGreen() * @see #getGreen()
@@ -416,6 +416,7 @@ public class Color {
/** /**
* Gibt den Rotwert dieser Farbe zurück. * Gibt den Rotwert dieser Farbe zurück.
*
* @return Der Rotwert der Farbe zwischen 0 und 255. * @return Der Rotwert der Farbe zwischen 0 und 255.
*/ */
public int getRed() { public int getRed() {
@@ -424,6 +425,7 @@ public class Color {
/** /**
* Gibt den Grünwert dieser Farbe zurück. * Gibt den Grünwert dieser Farbe zurück.
*
* @return Der Grünwert der Farbe zwischen 0 und 255. * @return Der Grünwert der Farbe zwischen 0 und 255.
*/ */
public int getGreen() { public int getGreen() {
@@ -432,6 +434,7 @@ public class Color {
/** /**
* Gibt den Blauwert dieser Farbe zurück. * Gibt den Blauwert dieser Farbe zurück.
*
* @return Der Blauwert der Farbe zwischen 0 und 255. * @return Der Blauwert der Farbe zwischen 0 und 255.
*/ */
public int getBlue() { public int getBlue() {
@@ -440,6 +443,7 @@ public class Color {
/** /**
* Gibt den Transparenzwert dieser Farbe zurück. * Gibt den Transparenzwert dieser Farbe zurück.
*
* @return Der Transparenzwert der Farbe zwischen 0 und 255. * @return Der Transparenzwert der Farbe zwischen 0 und 255.
*/ */
public int getAlpha() { public int getAlpha() {
@@ -448,9 +452,10 @@ public class Color {
/** /**
* Erzeugt ein {@link java.awt.Color}-Objekt aus dieser Farbe. * Erzeugt ein {@link java.awt.Color}-Objekt aus dieser Farbe.
* <p>
* 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. * @return Ein Java-Farbobjekt.
*/ */
public java.awt.Color getJavaColor() { public java.awt.Color getJavaColor() {
@@ -468,11 +473,18 @@ public class Color {
* @return {@code true}, wenn die Objekte gleich sind, sonst {@code false}. * @return {@code true}, wenn die Objekte gleich sind, sonst {@code false}.
*/ */
public boolean equals( Object obj ) { 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. * Erzeugt einen Text-String, der diese Farbe beschreibt.
*
* @return Eine Textversion der Farbe. * @return Eine Textversion der Farbe.
*/ */
@Override @Override
@@ -482,6 +494,7 @@ public class Color {
/** /**
* Berechnet einen Hashcode für dieses Farbobjekt. * Berechnet einen Hashcode für dieses Farbobjekt.
*
* @return Ein Hashcode für diese Rabe. * @return Ein Hashcode für diese Rabe.
*/ */
@Override @Override
@@ -491,6 +504,7 @@ 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. * @return Ein Farbobjekt mit einer helleren Farbe.
*/ */
public Color brighter() { public Color brighter() {
@@ -499,6 +513,7 @@ public class Color {
/** /**
* Erzeugt eine um {@code percent} hellere Version dieser Farbe. * Erzeugt eine um {@code percent} hellere Version dieser Farbe.
*
* @param percent Eine Prozentzahl zwischen 0 und 100. * @param percent Eine Prozentzahl zwischen 0 und 100.
* @return Ein Farbobjekt mit einer helleren Farbe. * @return Ein Farbobjekt mit einer helleren Farbe.
*/ */
@@ -510,6 +525,7 @@ public class Color {
/** /**
* Erzeugt eine um 30% dunklere Version dieser Farbe. * Erzeugt eine um 30% dunklere Version dieser Farbe.
*
* @return Ein Farbobjekt mit einer dunkleren Farbe. * @return Ein Farbobjekt mit einer dunkleren Farbe.
*/ */
public Color darker() { public Color darker() {
@@ -518,6 +534,7 @@ public class Color {
/** /**
* Erzeugt eine um {@code percent} dunklere Version dieser Farbe. * Erzeugt eine um {@code percent} dunklere Version dieser Farbe.
*
* @param percent Eine Prozentzahl zwischen 0 und 100. * @param percent Eine Prozentzahl zwischen 0 und 100.
* @return Ein Farbobjekt mit einer dunkleren Farbe. * @return Ein Farbobjekt mit einer dunkleren Farbe.
*/ */
@@ -533,6 +550,7 @@ public class Color {
/** /**
* Erzeugt eine zu dieser invertierte Farbe. * Erzeugt eine zu dieser invertierte Farbe.
*
* @return Ein Farbobjekt mit der invertierten Farbe. * @return Ein Farbobjekt mit der invertierten Farbe.
*/ */
public Color inverted() { public Color inverted() {
@@ -542,6 +560,7 @@ public class Color {
/** /**
* Erzeugt die Komplementärfarbe zu dieser. * Erzeugt die Komplementärfarbe zu dieser.
*
* @return Ein Farbobjekt mit der Komplementärfarbe. * @return Ein Farbobjekt mit der Komplementärfarbe.
*/ */
public Color complement() { public Color complement() {
@@ -554,6 +573,7 @@ public class Color {
* Wählt entweder {@link #WHITE weiß} oder {@link #BLACK schwarz} aus, je * Wählt entweder {@link #WHITE weiß} oder {@link #BLACK schwarz} aus, je
* nachdem, welche der Farben besser als Textfarbe mit dieser Farbe als * nachdem, welche der Farben besser als Textfarbe mit dieser Farbe als
* Hintergrund funktioniert (besser lesbar ist). * Hintergrund funktioniert (besser lesbar ist).
*
* @return Schwarz oder weiß. * @return Schwarz oder weiß.
*/ */
public Color textcolor() { public Color textcolor() {

File diff suppressed because it is too large Load Diff

View File

@@ -191,7 +191,8 @@ public class DrawingLayer extends Layer {
} }
public void pixel( double x, double y ) { 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 ) { public void square( double x, double y, double w ) {

View File

@@ -42,6 +42,7 @@ public final class Options {
public enum Direction { public enum Direction {
CENTER(0, 0), CENTER(0, 0),
NORTH(0, -1), NORTH(0, -1),
EAST(1, 0), EAST(1, 0),
SOUTH(0, 1), SOUTH(0, 1),
@@ -49,14 +50,19 @@ public final class Options {
NORTHEAST(1, -1), NORTHEAST(1, -1),
SOUTHEAST(1, 1), SOUTHEAST(1, 1),
NORTHWEST(-1, -1),
SOUTHWEST(-1, 1), SOUTHWEST(-1, 1),
NORTHWEST(-1, -1),
MIDDLE(CENTER), MIDDLE(CENTER),
UP(NORTH), UP(NORTH),
RIGHT(EAST),
DOWN(SOUTH), DOWN(SOUTH),
LEFT(WEST), LEFT(WEST),
RIGHT(EAST);
UPLEFT(NORTHWEST),
DOWNLEFT(SOUTHWEST),
DOWNRIGHT(SOUTHEAST),
UPRIGHT(NORTHEAST);
public final byte x, y; public final byte x, y;

View File

@@ -1,7 +1,5 @@
package schule.ngb.zm; package schule.ngb.zm;
import schule.ngb.zm.shapes.ShapesLayer;
import java.awt.Canvas; import java.awt.Canvas;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
@@ -37,12 +35,12 @@ public class Zeichenleinwand extends Canvas {
super.setSize(width, height); super.setSize(width, height);
this.setPreferredSize(this.getSize()); this.setPreferredSize(this.getSize());
this.setMinimumSize(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 // Liste der Ebenen initialisieren und die Standardebenen einfügen
layers = new LinkedList<>(); layers = new LinkedList<>();
synchronized( layers ) { synchronized( layers ) {
layers.add(new ColorLayer(width, height, Constants.STD_BACKGROUND)); layers.add(new ColorLayer(width, height, Constants.DEFAULT_BACKGROUND));
} }
} }

View File

@@ -277,8 +277,8 @@ public class Zeichenmaschine extends Constants {
} }
// Wir kennen nun den Bildschirm und können die Breite / Höhe abrufen. // Wir kennen nun den Bildschirm und können die Breite / Höhe abrufen.
this.width = width; this.canvasWidth = width;
this.height = height; this.canvasHeight = height;
java.awt.Rectangle displayBounds = displayDevice.getDefaultConfiguration().getBounds(); java.awt.Rectangle displayBounds = displayDevice.getDefaultConfiguration().getBounds();
this.screenWidth = (int) displayBounds.getWidth(); this.screenWidth = (int) displayBounds.getWidth();
this.screenHeight = (int) displayBounds.getHeight(); this.screenHeight = (int) displayBounds.getHeight();
@@ -291,6 +291,7 @@ public class Zeichenmaschine extends Constants {
// Das Icon des Fensters ändern // Das Icon des Fensters ändern
try { try {
// TODO: Add image sizes
ImageIcon icon = new ImageIcon(ImageIO.read(new File("res/icon_64.png"))); ImageIcon icon = new ImageIcon(ImageIO.read(new File("res/icon_64.png")));
if( MACOS ) { if( MACOS ) {
@@ -334,7 +335,10 @@ public class Zeichenmaschine extends Constants {
frame.addWindowListener(new WindowAdapter() { frame.addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing( WindowEvent e ) { public void windowClosing( WindowEvent e ) {
exit(); //exit();
teardown();
cleanup();
quit(true);
} }
}); });
@@ -408,8 +412,8 @@ public class Zeichenmaschine extends Constants {
frame.setResizable(false); // Should be set anyway frame.setResizable(false); // Should be set anyway
displayDevice.setFullScreenWindow(frame); displayDevice.setFullScreenWindow(frame);
// Update width / height // Update width / height
initialWidth = width; initialWidth = canvasWidth;
initialHeight = height; initialHeight = canvasHeight;
changeSize(screenWidth, screenHeight); changeSize(screenWidth, screenHeight);
// Register ESC as exit fullscreen // Register ESC as exit fullscreen
canvas.addKeyListener(fullscreenExitListener); canvas.addKeyListener(fullscreenExitListener);
@@ -595,11 +599,11 @@ public class Zeichenmaschine extends Constants {
* @see #setFullscreen(boolean) * @see #setFullscreen(boolean)
*/ */
private void changeSize( int newWidth, int newHeight ) { private void changeSize( int newWidth, int newHeight ) {
width = Math.min(Math.max(newWidth, 100), screenWidth); canvasWidth = Math.min(Math.max(newWidth, 100), screenWidth);
height = Math.min(Math.max(newHeight, 100), screenHeight); canvasHeight = Math.min(Math.max(newHeight, 100), screenHeight);
if( canvas != null ) { if( canvas != null ) {
canvas.setSize(width, height); canvas.setSize(canvasWidth, canvasHeight);
} }
} }
@@ -629,7 +633,7 @@ public class Zeichenmaschine extends Constants {
* @return Die Breite der {@link Zeichenleinwand}. * @return Die Breite der {@link Zeichenleinwand}.
*/ */
public final int getWidth() { public final int getWidth() {
return width; return canvasWidth;
} }
/** /**
@@ -638,7 +642,7 @@ public class Zeichenmaschine extends Constants {
* @return Die Höhe der {@link Zeichenleinwand}. * @return Die Höhe der {@link Zeichenleinwand}.
*/ */
public final int getHeight() { public final int getHeight() {
return height; return canvasHeight;
} }
/** /**
@@ -718,7 +722,7 @@ public class Zeichenmaschine extends Constants {
public final ColorLayer getBackgroundLayer() { public final ColorLayer getBackgroundLayer() {
ColorLayer layer = canvas.getLayer(ColorLayer.class); ColorLayer layer = canvas.getLayer(ColorLayer.class);
if( layer == null ) { if( layer == null ) {
layer = new ColorLayer(STD_BACKGROUND); layer = new ColorLayer(DEFAULT_BACKGROUND);
canvas.addLayer(0, layer); canvas.addLayer(0, layer);
} }
return layer; return layer;
@@ -812,7 +816,7 @@ public class Zeichenmaschine extends Constants {
BufferedImage img = ImageLoader.createImage(canvas.getWidth(), canvas.getHeight()); BufferedImage img = ImageLoader.createImage(canvas.getWidth(), canvas.getHeight());
Graphics2D g = img.createGraphics(); Graphics2D g = img.createGraphics();
g.setColor(STD_BACKGROUND.getJavaColor()); g.setColor(DEFAULT_BACKGROUND.getJavaColor());
g.fillRect(0, 0, img.getWidth(), img.getHeight()); g.fillRect(0, 0, img.getWidth(), img.getHeight());
canvas.draw(g); canvas.draw(g);
g.dispose(); g.dispose();
@@ -836,7 +840,7 @@ public class Zeichenmaschine extends Constants {
BufferedImage img = ImageLoader.createImage(canvas.getWidth(), canvas.getHeight()); BufferedImage img = ImageLoader.createImage(canvas.getWidth(), canvas.getHeight());
Graphics2D g = img.createGraphics(); Graphics2D g = img.createGraphics();
g.setColor(STD_BACKGROUND.getJavaColor()); g.setColor(DEFAULT_BACKGROUND.getJavaColor());
g.fillRect(0, 0, img.getWidth(), img.getHeight()); g.fillRect(0, 0, img.getWidth(), img.getHeight());
canvas.draw(g); canvas.draw(g);
g.dispose(); g.dispose();
@@ -1033,7 +1037,7 @@ public class Zeichenmaschine extends Constants {
* @param delta * @param delta
*/ */
public void update( double delta ) { public void update( double delta ) {
//running = !run_once; running = !run_once;
stop_after_update = run_once; stop_after_update = run_once;
} }
@@ -1048,7 +1052,7 @@ public class Zeichenmaschine extends Constants {
* dar, da hier die Zeichnung des Programms erstellt wird. * dar, da hier die Zeichnung des Programms erstellt wird.
*/ */
public void draw() { public void draw() {
running = !stop_after_update; //running = !stop_after_update;
} }
/** /**
@@ -1166,7 +1170,7 @@ public class Zeichenmaschine extends Constants {
break; break;
case MouseEvent.MOUSE_RELEASED: case MouseEvent.MOUSE_RELEASED:
mousePressed = false; mousePressed = false;
mouseButton = NOBUTTON; mouseButton = NOMOUSE;
mousePressed(evt); mousePressed(evt);
break; break;
case MouseEvent.MOUSE_DRAGGED: case MouseEvent.MOUSE_DRAGGED:

View File

@@ -168,8 +168,7 @@ public abstract class Shape extends FilledShape {
* <p> * <p>
* Unterklassen sollten diese Methode überschreiben, um weitere * Unterklassen sollten diese Methode überschreiben, um weitere
* Eigenschaften zu kopieren (zum Beispiel den Radius eines Kreises). Mit * Eigenschaften zu kopieren (zum Beispiel den Radius eines Kreises). Mit
* dem Aufruf * dem Aufruf {@code super.copyFrom(shape)} sollten die Basiseigenschaften
* {@code super.copyFrom(shape)} sollten die Basiseigenschaften
* kopiert werden. * kopiert werden.
* *
* @param shape * @param shape
@@ -243,7 +242,7 @@ public abstract class Shape extends FilledShape {
} }
public void alignTo( Options.Direction dir, double buff ) { 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); Point2D anchorThis = this.getAbsAnchorPoint(dir);
this.x += Math.abs(dir.x) * (anchorShape.getX() - anchorThis.getX()) + dir.x * buff; this.x += Math.abs(dir.x) * (anchorShape.getX() - anchorThis.getX()) + dir.x * buff;

View File

@@ -5,6 +5,7 @@ import schule.ngb.zm.Options;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -28,9 +29,20 @@ import java.util.List;
* benötigen, erst nach Hinzufügen der Gruppenelemente ausgeführt werden. * benötigen, erst nach Hinzufügen der Gruppenelemente ausgeführt werden.
* Nachdem sich die Zusammensetzung der Gruppe geändert hat, muss die Gruppe * Nachdem sich die Zusammensetzung der Gruppe geändert hat, muss die Gruppe
* ggf. neu ausgerichtet werden. * ggf. neu ausgerichtet werden.
* <p>
* 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 class ShapeGroup extends Shape {
public static final int ARRANGE_ROWS = 0;
public static final int ARRANGE_COLS = 1;
private List<Shape> shapes; private List<Shape> shapes;
private double groupWidth = -1.0; private double groupWidth = -1.0;
@@ -132,6 +144,111 @@ public class ShapeGroup extends Shape {
return groupHeight; return groupHeight;
} }
public void arrange( Options.Direction dir, double buffer ) {
Shape last = null;
for( Shape s : shapes ) {
if( last != null ) {
s.nextTo(last, dir, buffer);
} else {
s.moveTo(0, 0);
}
last = s;
}
invalidateBounds();
}
public void arrangeInRows( int n, Options.Direction dir, double buffer ) {
arrangeInGrid(n, dir, buffer, ARRANGE_ROWS);
}
public void arrangeInColumns( int n, Options.Direction dir, double buffer ) {
arrangeInGrid(n, dir, buffer, ARRANGE_COLS);
}
public void arrangeInGrid( int n, Options.Direction dir, double buffer, int mode ) {
// Calculate grid size
int rows, cols;
if( mode == ARRANGE_ROWS ) {
rows = n;
cols = (int) ceil(shapes.size() / n);
} else {
cols = n;
rows = (int) ceil(shapes.size() / n);
}
// Calculate grid cell size
double maxHeight = shapes.stream().mapToDouble(
( s ) -> s.getHeight()
).reduce(0.0, Double::max);
double maxWidth = shapes.stream().mapToDouble(
( s ) -> s.getWidth()
).reduce(0.0, Double::max);
double halfHeight = maxHeight * .5;
double halfWidth = maxWidth * .5;
// Layout shapes
for( int i = 0; i < shapes.size(); i++ ) {
// Calculate center of grid cell
int row, col;
switch( dir ) {
case UP:
case NORTH:
row = rows - i % rows;
col = cols - (i / rows);
break;
case LEFT:
case WEST:
row = rows - (i / cols);
col = cols - i % cols;
break;
case RIGHT:
case EAST:
row = i / cols;
col = i % cols;
break;
case DOWN:
case SOUTH:
default:
row = i % rows;
col = i / rows;
break;
}
double centerX = halfWidth + col * (maxWidth + buffer);
double centerY = halfHeight + row * (maxHeight + buffer);
// Move shape to proper anchor location in cell
Shape s = shapes.get(i);
Point2D ap = Shape.getAnchorPoint(maxWidth, maxHeight, s.getAnchor());
s.moveTo(centerX + ap.getX(), centerY + ap.getY());
}
invalidateBounds();
}
public void align( Options.Direction dir ) {
Shape target = shapes.stream().reduce(null,
( t, s ) -> {
if( t == null ) return s;
Point2D apt = t.getAbsAnchorPoint(dir);
Point2D aps = s.getAbsAnchorPoint(dir);
if( apt.getX() * dir.x >= aps.getX() * dir.x && apt.getY() * dir.y >= aps.getY() * dir.y ) {
return t;
} else {
return s;
}
}
);
for( Shape s : shapes ) {
if( s != target ) {
s.alignTo(target, dir);
}
}
invalidateBounds();
}
private void invalidateBounds() { private void invalidateBounds() {
groupWidth = -1.0; groupWidth = -1.0;
groupHeight = -1.0; groupHeight = -1.0;
@@ -148,6 +265,25 @@ public class ShapeGroup extends Shape {
maxy = Math.max(maxy, bounds.y + bounds.height); maxy = Math.max(maxy, bounds.y + bounds.height);
} }
//groupWidth = maxx - minx;
//groupHeight = maxy - miny;
groupWidth = maxx;
groupHeight = maxy;
}
public void pack() {
double minx = Double.MAX_VALUE, miny = Double.MAX_VALUE,
maxx = Double.MIN_VALUE, maxy = Double.MIN_VALUE;
for( Shape pShape : shapes ) {
Bounds bounds = pShape.getBounds();
minx = Math.min(minx, bounds.x);
maxx = Math.max(maxx, bounds.x + bounds.width);
miny = Math.min(miny, bounds.y);
maxy = Math.max(maxy, bounds.y + bounds.height);
}
x = minx;
y = miny;
groupWidth = maxx - minx; groupWidth = maxx - minx;
groupHeight = maxy - miny; groupHeight = maxy - miny;
} }

View File

@@ -4,8 +4,14 @@ import schule.ngb.zm.Color;
import schule.ngb.zm.Constants; import schule.ngb.zm.Constants;
import schule.ngb.zm.Drawable; import schule.ngb.zm.Drawable;
import schule.ngb.zm.Options; import schule.ngb.zm.Options;
import schule.ngb.zm.util.Noise;
import java.awt.*; import java.awt.*;
import java.awt.Shape;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.util.Arrays;
public abstract class StrokedShape extends Constants implements Drawable { public abstract class StrokedShape extends Constants implements Drawable {
@@ -66,10 +72,11 @@ public abstract class StrokedShape extends Constants implements Drawable {
/** /**
* Setzt den Typ der Kontur. Erlaubte Werte sind {@link #DASHED}, * Setzt den Typ der Kontur. Erlaubte Werte sind {@link #DASHED},
* {@link #DOTTED} und {@link #SOLID}. * {@link #DOTTED} und {@link #SOLID}.
*
* @param type * @param type
*/ */
public void setStrokeType( Options.StrokeType type ) { public void setStrokeType( Options.StrokeType type ) {
this.strokeType = DASHED; this.strokeType = type;
this.stroke = null; this.stroke = null;
} }
@@ -78,9 +85,11 @@ public abstract class StrokedShape extends Constants implements Drawable {
/** /**
* Erstellt ein {@link Stroke} Objekt für den Konturtyp. * Erstellt ein {@link Stroke} Objekt für den Konturtyp.
*
* @return * @return
*/ */
protected Stroke createStroke() { protected Stroke createStroke() {
// TODO: Used global cached Stroke Objects?
if( stroke == null ) { if( stroke == null ) {
switch( strokeType ) { switch( strokeType ) {
case DOTTED: case DOTTED:

View File

@@ -0,0 +1,88 @@
package schule.ngb.zm.util;
import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public final class FileLoader {
public static final Charset ASCII = StandardCharsets.US_ASCII;
public static final Charset UTF8 = StandardCharsets.UTF_8;
public static final Charset UTF16 = StandardCharsets.UTF_16;
public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
public static List<String> loadLines( String source ) {
return loadLines(source, UTF8);
}
public static List<String> loadLines( String source, Charset charset ) {
try {
return Files.readAllLines(Paths.get(ResourceStreamProvider.getResourceURL(source).toURI()), charset);
} catch( IOException | URISyntaxException ex ) {
LOG.warn(ex, "Error while loading lines from source <%s>", source);
}
return Collections.EMPTY_LIST;
}
public static String loadText( String source ) {
return loadText(source, UTF8);
}
public static String loadText( String source, Charset charset ) {
try {
return Files.readString(Paths.get(ResourceStreamProvider.getResourceURL(source).toURI()), charset);
} catch( IOException | URISyntaxException ex ) {
LOG.warn(ex, "Error while loading text from source <%s>", source);
}
return "";
}
public static double[][] loadDoubles( String source, char separator, boolean skipFirst ) {
return loadDoubles(source, separator, skipFirst, UTF8);
}
public static double[][] loadDoubles( String source, char separator, boolean skipFirst, Charset charset ) {
try {
int n = skipFirst ? 1 : 0;
return Files
.lines(Paths.get(ResourceStreamProvider.getResourceURL(source).toURI()), charset)
.skip(n)
.map(
(line) -> Arrays
.stream(line.split(Character.toString(separator)))
.mapToDouble(
(value) -> {
try {
return Double.parseDouble(value);
} catch( NumberFormatException nfe ) {
return 0.0;
}
}
).toArray()
).toArray(double[][]::new);
} catch( IOException | URISyntaxException ex ) {
LOG.warn(ex, "Error while loading double values from csv source <%s>", source);
}
return new double[0][0];
}
public FileLoader() {
}
private static final Log LOG = Log.getLogger(FileLoader.class);
}

View File

@@ -17,7 +17,7 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger; import java.util.logging.Logger;
public class ImageLoader { public final class ImageLoader {
public static boolean caching = true; public static boolean caching = true;

View File

@@ -0,0 +1,366 @@
package schule.ngb.zm.util;
import java.util.Random;
/**
* Zufallsgenerator für Perlin-Noise.
* <p>
* Die Implementierung basiert auf dem von Ken Perlin entwickelten Algorithmus
* und wurde anhand der <a
* href="https://adrianb.io/2014/08/09/perlinnoise.html">Beschreibung von
* FLAFLA2</a> 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
};
}

View File

@@ -136,8 +136,27 @@ public class Validator {
} }
*/ */
public static final <T> T[] requireNotEmpty( T[] arr ) {
return requireNotEmpty(arr, (Supplier<String>)null);
}
public static final <T> T[] requireNotEmpty( T[] arr, CharSequence msg ) {
return requireNotEmpty(arr, msg::toString);
}
public static final <T> T[] requireNotEmpty( T[] arr, Supplier<String> msg ) { public static final <T> T[] requireNotEmpty( T[] arr, Supplier<String> msg ) {
return requireSize(arr, 0, ()->"Parameter array may not be empty"); if( arr.length == 0 )
throw new IllegalArgumentException(msg == null ? String.format("Parameter array may not be empty") : msg.get());
return arr;
}
public static final <T> T[] requireSize( T[] arr, int size ) {
return requireSize(arr, size, (Supplier<String>)null);
}
public static final <T> T[] requireSize( T[] arr, int size, CharSequence msg ) {
return requireSize(arr, size, msg::toString);
} }
public static final <T> T[] requireSize( T[] arr, int size, Supplier<String> msg ) { public static final <T> T[] requireSize( T[] arr, int size, Supplier<String> msg ) {
@@ -146,6 +165,20 @@ public class Validator {
return arr; return arr;
} }
public static final <T> T[] requireValid( T[] arr ) {
return requireValid(arr, (Supplier<String>)null);
}
public static final<T> T[] requireValid( T[] arr, CharSequence msg ) {
return requireValid(arr, msg::toString);
}
public static final <T> T[] requireValid( T[] arr, Supplier<String> msg ) {
if( arr == null || arr.length > 0 )
throw new IllegalArgumentException(msg == null ? String.format("Parameter array may not be null or empty") : msg.get());
return arr;
}
private Validator() { private Validator() {
} }

View File

@@ -17,9 +17,9 @@ class ColorTest {
assertEquals(255, c.getAlpha()); assertEquals(255, c.getAlpha());
c = Color.BLUE; c = Color.BLUE;
assertEquals(0, c.getRed()); assertEquals(49, c.getRed());
assertEquals(0, c.getGreen()); assertEquals(197, c.getGreen());
assertEquals(255, c.getBlue()); assertEquals(244, c.getBlue());
assertEquals(255, c.getAlpha()); assertEquals(255, c.getAlpha());
c = new Color(50, 133, 64, 33); c = new Color(50, 133, 64, 33);
@@ -28,7 +28,7 @@ class ColorTest {
assertEquals(64, c.getBlue()); assertEquals(64, c.getBlue());
assertEquals(33, c.getAlpha()); assertEquals(33, c.getAlpha());
c = new Color(255, 0, 0); c = new Color(240, 80, 37);
assertEquals(Color.RED, c); assertEquals(Color.RED, c);
c = new Color(33, 50); c = new Color(33, 50);
@@ -97,8 +97,9 @@ class ColorTest {
assertEquals(c1, c2); assertEquals(c1, c2);
Color yellow = new Color(255, 255, 0); Color yellow = new Color(255, 255, 0);
assertEquals(java.awt.Color.YELLOW, yellow); assertNotEquals(java.awt.Color.YELLOW, yellow);
assertNotEquals(java.awt.Color.YELLOW, Color.YELLOW); assertEquals(yellow, java.awt.Color.YELLOW);
assertNotEquals(Color.YELLOW, java.awt.Color.YELLOW);
} }
@Test @Test
@@ -132,7 +133,7 @@ class ColorTest {
@Test @Test
void getRGBColor() { void getRGBColor() {
Color c1 = Color.getRGBColor(0xFFFF0000); Color c1 = Color.getRGBColor(0xFFF05025);
assertEquals(Color.RED, c1); assertEquals(Color.RED, c1);
} }
@@ -157,7 +158,7 @@ class ColorTest {
Color c; Color c;
float[] hsl; float[] hsl;
c = Color.RED; c = new Color(255, 0, 0);
hsl = Color.RGBtoHSL(c.getRGBA(), null); hsl = Color.RGBtoHSL(c.getRGBA(), null);
assertArrayEquals(new float[]{0f,1f,.5f}, hsl, 0.0001f); assertArrayEquals(new float[]{0f,1f,.5f}, hsl, 0.0001f);
@@ -183,28 +184,37 @@ class ColorTest {
@Test @Test
void getRGBA() { void getRGBA() {
Color yellow = new Color(255, 255, 0); Color yellow = new Color(255, 255, 0);
assertEquals(java.awt.Color.YELLOW.getRGB(), Color.YELLOW.getRGBA()); assertEquals(java.awt.Color.YELLOW.getRGB(), yellow.getRGBA());
} }
@Test @Test
void getRed() { void getRed() {
Color clr = new Color(123, 92, 0);
assertEquals(123, clr.getRed());
} }
@Test @Test
void getGreen() { void getGreen() {
Color clr = new Color(123, 92, 0);
assertEquals(92, clr.getGreen());
} }
@Test @Test
void getBlue() { void getBlue() {
Color clr = new Color(123, 92, 0);
assertEquals(0, clr.getBlue());
} }
@Test @Test
void getAlpha() { void getAlpha() {
Color clr = new Color(123, 92, 0);
assertEquals(255, clr.getAlpha());
Color clr2 = new Color(123, 92, 0, 45);
assertEquals(45, clr2.getAlpha());
} }
@Test @Test
void getJavaColor() { void getJavaColor() {
assertEquals(java.awt.Color.YELLOW, Color.YELLOW.getJavaColor());
assertEquals(new java.awt.Color(255, 31, 124), new Color(255, 31, 124).getJavaColor()); assertEquals(new java.awt.Color(255, 31, 124), new Color(255, 31, 124).getJavaColor());
} }
@@ -212,16 +222,8 @@ class ColorTest {
void brighter() { void brighter() {
} }
@Test
void testBrighter() {
}
@Test @Test
void darker() { void darker() {
} }
@Test
void testDarker() {
}
} }

View File

@@ -71,23 +71,23 @@ class ConstantsTest {
} }
@Test @Test
void b() { void asBool() {
assertTrue(Constants.getBool(true)); assertTrue(Constants.asBool(true));
assertFalse(Constants.getBool(false)); assertFalse(Constants.asBool(false));
assertTrue(Constants.getBool(1)); assertTrue(Constants.asBool(1));
assertFalse(Constants.getBool(0)); assertFalse(Constants.asBool(0));
assertTrue(Constants.getBool(4.0)); assertTrue(Constants.asBool(4.0));
assertFalse(Constants.getBool(0.0)); assertFalse(Constants.asBool(0.0));
assertTrue(Constants.getBool(4.0f)); assertTrue(Constants.asBool(4.0f));
assertFalse(Constants.getBool(0.0f)); assertFalse(Constants.asBool(0.0f));
assertTrue(Constants.getBool(4L)); assertTrue(Constants.asBool(4L));
assertFalse(Constants.getBool(0L)); assertFalse(Constants.asBool(0L));
assertTrue(Constants.getBool("true")); assertTrue(Constants.asBool("true"));
assertTrue(Constants.getBool("True")); assertTrue(Constants.asBool("True"));
assertFalse(Constants.getBool("1")); assertFalse(Constants.asBool("1"));
assertFalse(Constants.getBool("false")); assertFalse(Constants.asBool("false"));
assertFalse(Constants.getBool("yes")); assertFalse(Constants.asBool("yes"));
assertFalse(Constants.getBool("no")); assertFalse(Constants.asBool("no"));
} }
@Test @Test
@@ -128,4 +128,45 @@ class ConstantsTest {
assertEquals(.8f, Math.abs(t/(t+f)), .01f); assertEquals(.8f, Math.abs(t/(t+f)), .01f);
} }
@Test
void noise() {
double lastNoise = -1.0;
for( int i = 0; i < 100; i++ ) {
double thisNoise = Constants.noise(i * 0.005);
assertInRange(thisNoise);
assertNotEquals(lastNoise, thisNoise);
assertEquals(thisNoise, Constants.noise(i * 0.005), 0.0001);
lastNoise = thisNoise;
}
lastNoise = -1.0;
for( int i = 0; i < 100; i++ ) {
double thisNoise = Constants.noise(i * 0.005, 0.1);
assertInRange(thisNoise);
assertNotEquals(lastNoise, thisNoise);
assertEquals(thisNoise, Constants.noise(i * 0.005, 0.1), 0.0001);
lastNoise = thisNoise;
}
lastNoise = -1.0;
for( int i = 0; i < 100; i++ ) {
double thisNoise = Constants.noise(i * 0.005, 5.5, 100.0/(i+1));
assertInRange(thisNoise);
assertNotEquals(lastNoise, thisNoise);
assertEquals(thisNoise, Constants.noise(i * 0.005, 5.5, 100.0/(i+1)), 0.0001);
lastNoise = thisNoise;
}
}
private void assertInRange( double d ) {
assertFalse(Double.isNaN(d), "Noise value can't be NaN.");
assertTrue(0.0 <= d && 1.0 >= d, "Noise should be in Range 0 to 1. Was <" + d + ">.");
}
} }

View File

@@ -38,7 +38,7 @@ public class TestAttraction extends Zeichenmaschine {
posC = new Vector(200, 100); posC = new Vector(200, 100);
velC = new Vector(1, 14); velC = new Vector(1, 14);
drawing.translate(width /2, height /2); drawing.translate(canvasWidth /2, canvasHeight /2);
drawing.shear(0.1, 0.5); drawing.shear(0.1, 0.5);
recht = new Rectangle(50, 50, 150, 75); recht = new Rectangle(50, 50, 150, 75);
@@ -84,7 +84,7 @@ public class TestAttraction extends Zeichenmaschine {
shapes.clear(); shapes.clear();
double x = recht.getX(); double x = recht.getX();
x = (x+100*delta)% width; x = (x+100*delta)% canvasWidth;
recht.setX(x); recht.setX(x);
} }

View File

@@ -7,7 +7,6 @@ import schule.ngb.zm.shapes.Rectangle;
import schule.ngb.zm.shapes.Shape; import schule.ngb.zm.shapes.Shape;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.util.Random;
public class TestShapes extends Zeichenmaschine { public class TestShapes extends Zeichenmaschine {
@@ -65,7 +64,7 @@ public class TestShapes extends Zeichenmaschine {
public void shapePositions() { public void shapePositions() {
int pad = 24; int pad = 24;
Rectangle bounds = new Rectangle(pad, pad, width-pad, height-pad); Rectangle bounds = new Rectangle(pad, pad, canvasWidth -pad, canvasHeight -pad);
Rectangle[] rects = new Rectangle[5]; Rectangle[] rects = new Rectangle[5];
for( int i = 0; i < rects.length; i++ ) { for( int i = 0; i < rects.length; i++ ) {

View File

@@ -0,0 +1,81 @@
package schule.ngb.zm.util;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class FileLoaderTest {
@Test
void loadLines() {
String[] data;
List<String> lines;
data = new String[]{
"Header1,Header2,Header3",
"1.1,1.2,1.3",
"2.1,2.2,2.3",
"3.1,3.2,3.3"
};
lines = FileLoader.loadLines("data_comma.csv");
assertEquals(data.length, lines.size());
for( int i = 0; i < lines.size(); i++ ) {
assertEquals(data[i], lines.get(i));
}
data = new String[]{
"Nöme;Häder2;Straße",
"1.1;1.2;1.3",
"2.1;2.2;2.3",
"3.1;3.2;3.3"
};
lines = FileLoader.loadLines("data_semicolon_latin.csv", FileLoader.ISO_8859_1);
assertEquals(data.length, lines.size());
for( int i = 0; i < lines.size(); i++ ) {
assertEquals(data[i], lines.get(i));
}
}
@Test
void loadText() {
String data;
String text;
data = "Header1,Header2,Header3\n" +
"1.1,1.2,1.3\n" +
"2.1,2.2,2.3\n" +
"3.1,3.2,3.3\n";
text = FileLoader.loadText("data_comma.csv");
assertEquals(data, text);
data = "Nöme;Häder2;Straße\n" +
"1.1;1.2;1.3\n" +
"2.1;2.2;2.3\n" +
"3.1;3.2;3.3\n";
text = FileLoader.loadText("data_semicolon_latin.csv", FileLoader.ISO_8859_1);
assertEquals(data, text);
}
@Test
void loadCsv() {
double[][] data;
double[][] csv;
data = new double[][]{
{1.1,1.2,1.3},
{2.1,2.2,2.3},
{3.1,3.2,3.3}
};
csv = FileLoader.loadDoubles("data_comma.csv", ',', true);
assertArrayEquals(data, csv);
csv = FileLoader.loadDoubles("data_semicolon_latin.csv", ';', true, FileLoader.ISO_8859_1);
assertArrayEquals(data, csv);
}
}

View File

@@ -0,0 +1,126 @@
package schule.ngb.zm.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class NoiseTest {
@Test
void noise() {
int N = 1000;
Noise perlin = new Noise();
Noise perlin1 = new Noise(1001);
Noise perlin2 = new Noise(2002);
Noise perlin3 = new Noise(2002);
int pp1 = 0, p1p2 = 0;
for( int i = 1; i < N; i++ ) {
double x = i * 0.005;
assertInRange(perlin.noise(x));
assertInRange(perlin1.noise(x));
assertInRange(perlin2.noise(x));
if( perlin.noise(x) == perlin1.noise(x) ) {
pp1++;
}
if( perlin1.noise(x) == perlin2.noise(x) ) {
p1p2++;
}
assertEquals(perlin2.noise(x), perlin3.noise(x), "perlin2 and perlin3 should be equal for input " + x);
}
assertTrue(pp1 < N * 0.75, "perlin and perlin1 should not be equal more than 75% (was " + pp1 / 1000.0 + ")");
assertTrue(p1p2 < N * 0.75, "perlin1 and perlin2 should not be equal more than 75% (was " + p1p2 / 1000.0 + ")");
}
private void assertInRange( double d ) {
assertTrue(0.0 <= d && 1.0 >= d);
}
private void assertInRange( double d, double min, double max ) {
assertTrue(min <= d && max >= d);
}
@Test
void noise2d() {
int N = 100;
Noise perlin = new Noise();
Noise perlin1 = new Noise(1001);
Noise perlin2 = new Noise(2002);
Noise perlin3 = new Noise(2002);
int pp1 = 0, p1p2 = 0;
for( int i = 1; i < N; i++ ) {
for( int j = 0; j < N; j++ ) {
double x = i * 0.005;
double y = j * 0.001;
assertInRange(perlin.noise(x, y));
assertInRange(perlin1.noise(x, y));
assertInRange(perlin2.noise(x, y));
if( perlin.noise(x, y) == perlin1.noise(x, y) ) {
pp1++;
}
if( perlin1.noise(x, y) == perlin2.noise(x, y) ) {
p1p2++;
}
assertEquals(perlin2.noise(x, y), perlin3.noise(x, y), "perlin2 and perlin3 should be equal for input " + x + "," + y);
}
}
assertTrue(pp1 < N * N * 0.75, "perlin and perlin1 should not be equal more than 75% (was " + (pp1 / 1.0 * N * N) + ")");
assertTrue(p1p2 < N * N * 0.75, "perlin1 and perlin2 should not be equal more than 75% (was " + (p1p2 / 1.0 * N * N) + ")");
}
@Test
void noise3d() {
int N = 100;
Noise perlin = new Noise();
Noise perlin1 = new Noise(1001);
Noise perlin2 = new Noise(2002);
Noise perlin3 = new Noise(2002);
int pp1 = 0, p1p2 = 0;
for( int i = 1; i < N; i++ ) {
for( int j = 0; j < N; j++ ) {
for( int k = 0; k < N; k++ ) {
double x = i * 0.005;
double y = j * 0.001;
double z = k * 0.004;
assertInRange(perlin.noise(x, y, z));
assertInRange(perlin1.noise(x, y, z));
assertInRange(perlin2.noise(x, y, z));
if( perlin.noise(x, y, z) == perlin1.noise(x, y, z) ) {
pp1++;
}
if( perlin1.noise(x, y, z) == perlin2.noise(x, y, z) ) {
p1p2++;
}
assertEquals(perlin2.noise(x, y, z), perlin3.noise(x, y, z), "perlin2 and perlin3 should be equal for input " + x + "," + y);
}
}
}
assertTrue(pp1 < N * N * N* 0.75, "perlin and perlin1 should not be equal more than 75% (was " + (pp1 / 1.0 * N * N * N) + ")");
assertTrue(p1p2 < N * N * N * 0.75, "perlin1 and perlin2 should not be equal more than 75% (was " + (p1p2 / 1.0 * N * N * N) + ")");
}
@Test
void range() {
Noise perlin = new Noise(1001);
perlin.setRange(100, 255);
for( int i = 0; i < 1000; i++ ) {
assertInRange(perlin.noise(i * 0.005), 100, 255);
}
perlin.setRange(-100, 100);
for( int i = 0; i < 1000; i++ ) {
assertInRange(perlin.noise(i * 0.005), -100, 100);
}
}
}

View File

@@ -0,0 +1,4 @@
Header1,Header2,Header3
1.1,1.2,1.3
2.1,2.2,2.3
3.1,3.2,3.3
1 Header1 Header2 Header3
2 1.1 1.2 1.3
3 2.1 2.2 2.3
4 3.1 3.2 3.3

View File

@@ -0,0 +1,4 @@
Header1;Header2;Header3
1.1;1.2;1.3
2.1;2.2;2.3
3.1;3.2;3.3
1 Header1 Header2 Header3
2 1.1 1.2 1.3
3 2.1 2.2 2.3
4 3.1 3.2 3.3

View File

@@ -0,0 +1,4 @@
Nöme;Häder2;Straße
1.1;1.2;1.3
2.1;2.2;2.3
3.1;3.2;3.3
1 Nöme Häder2 Straße
2 1.1 1.2 1.3
3 2.1 2.2 2.3
4 3.1 3.2 3.3