mirror of
https://github.com/jneug/zeichenmaschine.git
synced 2026-10-12 01:45:18 +02:00
Merge branch 'main' into events
# Conflicts: # src/main/java/schule/ngb/zm/media/AudioListener.java # src/main/java/schule/ngb/zm/media/Mixer.java # src/main/java/schule/ngb/zm/media/Music.java # src/schule/ngb/zm/media/Sound.java
This commit is contained in:
@@ -3,12 +3,12 @@ package schule.ngb.zm;
|
||||
/**
|
||||
* Repräsentiert eine Farbe in der Zeichenmaschine.
|
||||
* <p>
|
||||
* Farben bestehen entweder aus einem Grauwert (zwischen <code>0</code> und
|
||||
* <code>255</code>) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen
|
||||
* <code>0</code> und <code>255</code>).
|
||||
* Farben bestehen entweder aus einem Grauwert (zwischen 0 und
|
||||
* 255) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen
|
||||
* 0 und 255).
|
||||
* <p>
|
||||
* Eine Farbe hat außerdem einen Transparenzwert zwischen <code>0</code>
|
||||
* (unsichtbar) und <code>255</code> (deckend).
|
||||
* Eine Farbe hat außerdem einen Transparenzwert zwischen 0
|
||||
* (unsichtbar) und 255 (deckend).
|
||||
*/
|
||||
public class Color {
|
||||
|
||||
@@ -106,7 +106,7 @@ public class Color {
|
||||
/**
|
||||
* Erstellt eine graue Farbe entsprechend des Grauwertes <var>gray</var>.
|
||||
*
|
||||
* @param gray Ein Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||
*/
|
||||
public Color( int gray ) {
|
||||
this(gray, gray, gray, 255);
|
||||
@@ -116,7 +116,7 @@ public class Color {
|
||||
* Erstellt eine graue Farbe entsprechend des Grauwertes <var>gray</var> und
|
||||
* des Transparentwertes <var>alpha</var>.
|
||||
*
|
||||
* @param gray Ein Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||
*/
|
||||
public Color( int gray, int alpha ) {
|
||||
this(gray, gray, gray, alpha);
|
||||
@@ -125,12 +125,11 @@ public class Color {
|
||||
/**
|
||||
* 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
|
||||
* liegen zwischen <code>0</code> und <code>255</code>.
|
||||
* liegen zwischen 0 und 255.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
* @param red Rotwert zwischen 0 und 255.
|
||||
* @param green Grünwert zwischen 0 und 255.
|
||||
* @param blue Blauwert zwischen 0 und 255.
|
||||
*/
|
||||
public Color( int red, int green, int blue ) {
|
||||
this(red, green, blue, 255);
|
||||
@@ -139,17 +138,16 @@ public class Color {
|
||||
/**
|
||||
* 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
|
||||
* liegen zwischen <code>0</code> und <code>255</code>.
|
||||
* liegen zwischen 0 und 255.
|
||||
* <var>alpha</var> gibt den den Transparentwert an (auch zwischen
|
||||
* code>0</code> und <code>255</code>), wobei
|
||||
* <code>0</code> komplett durchsichtig ist und <code>255</code> komplett
|
||||
* 0 und 255), wobei
|
||||
* 0 komplett durchsichtig ist und 255 komplett
|
||||
* deckend.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param alpha Transparentwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
* @param red Rotwert zwischen 0 und 255.
|
||||
* @param green Grünwert 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;
|
||||
@@ -239,7 +237,7 @@ 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>#</code> Zeichen vorangestellt sein.
|
||||
* Dem Code kann ein {@code #} Zeichen vorangestellt sein.
|
||||
*
|
||||
* @param hexcode
|
||||
* @return
|
||||
@@ -387,10 +387,10 @@ public class Constants {
|
||||
|
||||
/**
|
||||
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
|
||||
* zwischen <code>0</code> und <code>255</code> an, wobei
|
||||
* <code>0</code> schwarz und <code>255</code> weiß ist.
|
||||
* zwischen 0 und 255 an, wobei
|
||||
* 0 schwarz und 255 weiß ist.
|
||||
*
|
||||
* @param gray Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param gray Grauwert zwischen 0 und 255.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int gray ) {
|
||||
@@ -399,16 +399,16 @@ public class Constants {
|
||||
|
||||
/**
|
||||
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
|
||||
* zwischen <code>0</code> und <code>255</code> an, wobei
|
||||
* <code>0</code> schwarz und <code>255</code> weiß ist.
|
||||
* zwischen 0 und 255 an, wobei
|
||||
* 0 schwarz und 255 weiß ist.
|
||||
* {@code alpha} gibt den den Transparentwert an (auch zwischen
|
||||
* <code>0</code> und <code>255</code>), wobei
|
||||
* <code>0</code> komplett durchsichtig ist und <code>255</code> komplett
|
||||
* 0 und 255), wobei
|
||||
* 0 komplett durchsichtig ist und 255 komplett
|
||||
* deckend.
|
||||
*
|
||||
* @param gray Grauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param alpha Transparentwert zwischen <code>0</code> und
|
||||
* <code>255</code>.
|
||||
* @param gray Grauwert zwischen 0 und 255.
|
||||
* @param alpha Transparentwert zwischen 0 und
|
||||
* 255.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int gray, int alpha ) {
|
||||
@@ -418,11 +418,11 @@ 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 <code>0</code> und <code>255</code>.
|
||||
* liegen zwischen 0 und 255.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param red Rotwert zwischen 0 und 255.
|
||||
* @param green Grünwert zwischen 0 und 255.
|
||||
* @param blue Blauwert zwischen 0 und 255.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int red, int green, int blue ) {
|
||||
@@ -432,17 +432,17 @@ 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 <code>0</code> und <code>255</code>. {@code alpha} gibt
|
||||
* den den Transparentwert an (auch zwischen code>0</code> und
|
||||
* <code>255</code>), wobei
|
||||
* <code>0</code> komplett durchsichtig ist und <code>255</code> komplett
|
||||
* 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.
|
||||
*
|
||||
* @param red Rotwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param green Grünwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param blue Blauwert zwischen <code>0</code> und <code>255</code>.
|
||||
* @param alpha Transparenzwert zwischen <code>0</code> und
|
||||
* <code>255</code>.
|
||||
* @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.
|
||||
* @return Ein passendes Farbobjekt.
|
||||
*/
|
||||
public static final Color color( int red, int green, int blue, int alpha ) {
|
||||
@@ -589,7 +589,7 @@ public class Constants {
|
||||
* Ermittelt das Vorzeichen der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>-1</code>, <code>1</code> oder <code>0</code>.
|
||||
* @return -1, 1 oder 0.
|
||||
*/
|
||||
public static final double sign( double x ) {
|
||||
return Math.signum(x);
|
||||
@@ -670,7 +670,7 @@ public class Constants {
|
||||
* Ermittelt den Sinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>sin(x)</code>.
|
||||
* @return {@code sin(x)}.
|
||||
*/
|
||||
public static final double sin( double x ) {
|
||||
return Math.sin(x);
|
||||
@@ -680,7 +680,7 @@ public class Constants {
|
||||
* Ermittelt den Kosinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>cos(x)</code>.
|
||||
* @return {@code cos(x)}.
|
||||
*/
|
||||
public static final double cos( double x ) {
|
||||
return Math.cos(x);
|
||||
@@ -690,7 +690,7 @@ public class Constants {
|
||||
* Ermittelt den Tangens der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>tan(x)</code>.
|
||||
* @return {@code tan(x)}.
|
||||
*/
|
||||
public static final double tan( double x ) {
|
||||
return Math.tan(x);
|
||||
@@ -700,7 +700,7 @@ public class Constants {
|
||||
* Ermittelt den Arkussinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>asin(x)</code>.
|
||||
* @return {@code asin(x)}.
|
||||
*/
|
||||
public static final double arcsin( double x ) {
|
||||
return Math.asin(x);
|
||||
@@ -710,7 +710,7 @@ public class Constants {
|
||||
* Ermittelt den Arkuskosinus der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>acos(x)</code>.
|
||||
* @return {@code acos(x)}.
|
||||
*/
|
||||
public static final double arccos( double x ) {
|
||||
return Math.acos(x);
|
||||
@@ -720,20 +720,20 @@ public class Constants {
|
||||
* Ermittelt den Arkusktangens der Zahl {@code x}.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @return <code>atan(x)</code>.
|
||||
* @return {@code atan(x)}.
|
||||
*/
|
||||
public static final double arctan( double x ) {
|
||||
return Math.atan(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall <code>[min, max]</code>.
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
|
||||
* Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
|
||||
* zurückgegeben.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @param max Das Maximum des Intervalls.
|
||||
* @return Eine Zahl im Intervall <code>[min, max]</code>.
|
||||
* @return Eine Zahl im Intervall {@code [min, max]}.
|
||||
*/
|
||||
public static final double limit( double x, double min, double max ) {
|
||||
if( x > max ) {
|
||||
@@ -746,13 +746,13 @@ public class Constants {
|
||||
}
|
||||
|
||||
/**
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall <code>[min, max]</code>.
|
||||
* Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
|
||||
* Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
|
||||
* zurückgegeben.
|
||||
*
|
||||
* @param x Eine Zahl.
|
||||
* @param max Das Maximum des Intervalls.
|
||||
* @return Eine Zahl im Intervall <code>[min, max]</code>.
|
||||
* @return Eine Zahl im Intervall {@code [min, max]}.
|
||||
*/
|
||||
public static final int limit( int x, int min, int max ) {
|
||||
if( x > max ) {
|
||||
@@ -774,7 +774,7 @@ public class Constants {
|
||||
*
|
||||
* @param from Startwert
|
||||
* @param to Zielwert
|
||||
* @param t Wert zwischen <code>0</code> und <code>1</code>.
|
||||
* @param t Wert zwischen 0 und 1.
|
||||
* @return Das Ergebnis der linearen Interpolation.
|
||||
*/
|
||||
public static final double interpolate( double from, double to, double t ) {
|
||||
@@ -885,8 +885,8 @@ public class Constants {
|
||||
* Erzeugt einen zufälligen Wahrheitswert. {@code true} wird mit der
|
||||
* Wahrscheinlichkeit {@code percent} Prozent erzeugt.
|
||||
*
|
||||
* @param percent Eine Prozentzahl zwischen <code>0</code> und
|
||||
* <code>100</code>.
|
||||
* @param percent Eine Prozentzahl zwischen 0 und
|
||||
* 100.
|
||||
* @return Ein Wahrheitswert.
|
||||
*/
|
||||
public static final boolean randomBool( int percent ) {
|
||||
@@ -12,7 +12,7 @@ public interface Drawable {
|
||||
* Gibt an, ob das Objekt derzeit sichtbar ist (also gezeichnet werden
|
||||
* muss).
|
||||
*
|
||||
* @return <code>true</code>, wenn das Objekt sichtbar ist.
|
||||
* @return {@code true}, wenn das Objekt sichtbar ist.
|
||||
*/
|
||||
boolean isVisible();
|
||||
|
||||
@@ -9,7 +9,7 @@ public interface Updatable {
|
||||
|
||||
/**
|
||||
* Gibt an, ob das Objekt gerade auf Aktualisierungen reagiert.
|
||||
* @return <code>true</code>, wenn das Objekt aktiv ist.
|
||||
* @return {@code true}, wenn das Objekt aktiv ist.
|
||||
*/
|
||||
public boolean isActive();
|
||||
|
||||
@@ -90,7 +90,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 <code>0</code>.
|
||||
* @param i Index der Ebene, beginnend mit 0.
|
||||
* @param layer Die neue Ebene.
|
||||
*/
|
||||
public void addLayer( int i, Layer layer ) {
|
||||
@@ -125,10 +125,10 @@ public class Zeichenleinwand extends Canvas {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt die Ebene am Index <var>i</var> (beginnend bei <code>0</code>).
|
||||
* Holt die Ebene am Index <var>i</var> (beginnend bei 0).
|
||||
*
|
||||
* @param i Index der Ebene (beginnend bei <code>0</code>).
|
||||
* @return Die Ebene am Index <var>i</var> oder <code>null</code>.
|
||||
* @param i Index der Ebene (beginnend bei 0).
|
||||
* @return Die Ebene am Index <var>i</var> oder {@code null}.
|
||||
* @throws IndexOutOfBoundsException Falls der Index nicht existiert.
|
||||
*/
|
||||
public Layer getLayer( int i ) {
|
||||
@@ -141,7 +141,7 @@ public class Zeichenleinwand extends Canvas {
|
||||
|
||||
/**
|
||||
* Sucht die erste Ebene des angegebenen Typs aus der Liste der Ebenen.
|
||||
* Existiert keine solche Ebene, wird <code>null</code> zurückgegeben.
|
||||
* Existiert keine solche Ebene, wird {@code null} zurückgegeben.
|
||||
*
|
||||
* @param clazz Typ der Ebene.
|
||||
* @param <L>
|
||||
@@ -531,6 +531,7 @@ public class Zeichenmaschine extends Constants {
|
||||
* vor {@code cleanup()} aufgerufen wird.
|
||||
*/
|
||||
private void cleanup() {
|
||||
LOG.debug("%s shutting down.", APP_NAME);
|
||||
// Alle noch nicht ausgelösten Events werden entfernt
|
||||
eventQueue.clear();
|
||||
// Alle noch nicht ausgeführten Tasks werden entfernt
|
||||
@@ -20,8 +20,8 @@ public interface Audio {
|
||||
* {@code isLooping() == true}, dann muss auch immer
|
||||
* {@code isPlaying() == true} gelten.
|
||||
*
|
||||
* @return @return {@code true}, wenn das Medium in einer Schleife
|
||||
* abgespielt wird, {@code false} sonst.
|
||||
* @return {@code true}, wenn das Medium in einer Schleife abgespielt wird,
|
||||
* {@code false} sonst.
|
||||
*/
|
||||
boolean isLooping();
|
||||
|
||||
@@ -38,17 +38,28 @@ public interface Audio {
|
||||
*/
|
||||
void setVolume( double volume );
|
||||
|
||||
/**
|
||||
* Gibt die aktuelle Lautstärkeeinstellung dieses Mediums zurück.
|
||||
* <p>
|
||||
* 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
|
||||
@@ -61,30 +61,53 @@ public class Mixer implements Audio {
|
||||
audios.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
@@ -94,16 +117,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() ) {
|
||||
@@ -112,6 +144,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.
|
||||
* <p>
|
||||
* 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
|
||||
@@ -119,9 +163,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;
|
||||
@@ -2,30 +2,58 @@ package schule.ngb.zm.media;
|
||||
|
||||
import schule.ngb.zm.events.EventDispatcher;
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Ein Musikstück, dass im Projekt abgespielt werden soll.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
EventDispatcher<Audio, AudioListener> events;
|
||||
@@ -36,6 +64,7 @@ public class Music implements Audio {
|
||||
}
|
||||
|
||||
public Music( String source ) {
|
||||
Validator.requireNotNull(source);
|
||||
this.audioSource = source;
|
||||
}
|
||||
|
||||
@@ -51,11 +80,17 @@ public class Music implements Audio {
|
||||
events.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return playing;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isLooping() {
|
||||
if( !playing ) {
|
||||
@@ -64,6 +99,9 @@ public class Music implements Audio {
|
||||
return looping;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setVolume( double volume ) {
|
||||
this.volume = (float) volume;
|
||||
@@ -72,6 +110,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);
|
||||
@@ -81,8 +131,11 @@ public class Music implements Audio {
|
||||
gainControl.setValue(vol);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playOnce() {
|
||||
public void play() {
|
||||
if( openLine() ) {
|
||||
TaskRunner.run(new Runnable() {
|
||||
@Override
|
||||
@@ -93,19 +146,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;
|
||||
@@ -113,6 +175,9 @@ public class Music implements Audio {
|
||||
dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
if( audioLine != null ) {
|
||||
@@ -130,7 +195,8 @@ public class Music implements Audio {
|
||||
if( audioStream != null ) {
|
||||
audioStream.close();
|
||||
}
|
||||
} catch( IOException ex ) {}
|
||||
} catch( IOException ex ) {
|
||||
}
|
||||
|
||||
audioLine = null;
|
||||
audioStream = null;
|
||||
@@ -145,15 +211,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
|
||||
@@ -176,14 +241,14 @@ public class Music implements Audio {
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
|
||||
if( in != null ) {
|
||||
final AudioInputStream inStream = AudioSystem.getAudioInputStream(in);
|
||||
URL url = ResourceStreamProvider.getResourceURL(audioSource);
|
||||
if( url != null ) {
|
||||
final AudioInputStream inStream = AudioSystem.getAudioInputStream(url);
|
||||
AudioFormat format = inStream.getFormat();
|
||||
|
||||
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);
|
||||
|
||||
@@ -194,28 +259,33 @@ 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;
|
||||
}
|
||||
|
||||
private void streamingStopped() {
|
||||
playing = false;
|
||||
dispose();
|
||||
|
||||
if( looping ) {
|
||||
playOnce();
|
||||
if( openLine() ) {
|
||||
stream();
|
||||
} else {
|
||||
playing = false;
|
||||
looping = false;
|
||||
}
|
||||
} else {
|
||||
playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
}
|
||||
309
src/main/java/schule/ngb/zm/media/Sound.java
Normal file
309
src/main/java/schule/ngb/zm/media/Sound.java
Normal file
@@ -0,0 +1,309 @@
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
return looping;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setVolume( double volume ) {
|
||||
this.volume = (float) volume;
|
||||
if( audioClip != null ) {
|
||||
applyVolume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
|
||||
float vol = 20f * (float) Math.log10(volume);
|
||||
// vol = (float) Constants.limit(vol, gainControl.getMinimum(), gainControl.getMaximum());
|
||||
gainControl.setValue(vol);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
looping = false;
|
||||
if( audioClip.isRunning() ) {
|
||||
audioClip.stop();
|
||||
}
|
||||
playing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void play() {
|
||||
if( this.openClip() ) {
|
||||
audioClip.start();
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void playAndWait() {
|
||||
this.play();
|
||||
|
||||
long audioLen = audioClip.getMicrosecondLength();
|
||||
while( playing ) {
|
||||
try {
|
||||
long ms = (audioLen - audioClip.getMicrosecondPosition()) / 1000L;
|
||||
Thread.sleep(ms);
|
||||
} catch( InterruptedException ex ) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
audioClip.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
|
||||
* frei.
|
||||
* <p>
|
||||
* Der Aufruf ist effektiv gleich zu
|
||||
* <pre><code>
|
||||
* clip.playAndWait();
|
||||
* clip.dispose();
|
||||
* </code></pre>
|
||||
* 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.
|
||||
* <p>
|
||||
* Der Aufruf entspricht
|
||||
* <pre><code>
|
||||
* clip.playAndWait();
|
||||
* clip.dispose();
|
||||
* </code></pre>
|
||||
*/
|
||||
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 ) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
if( audioClip != null ) {
|
||||
if( audioClip.isRunning() ) {
|
||||
audioClip.stop();
|
||||
}
|
||||
audioClip.close();
|
||||
audioClip = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean openClip() {
|
||||
if( audioClip != null ) {
|
||||
audioClip.setFramePosition(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
|
||||
if( in != null ) {
|
||||
AudioInputStream audioStream = AudioSystem.getAudioInputStream(in);
|
||||
AudioFormat format = audioStream.getFormat();
|
||||
DataLine.Info info = new DataLine.Info(Clip.class, format);
|
||||
|
||||
audioClip = (Clip) AudioSystem.getLine(info);
|
||||
audioClip.addLineListener(new LineListener() {
|
||||
@Override
|
||||
public void update( LineEvent event ) {
|
||||
if( event.getType() == LineEvent.Type.STOP ) {
|
||||
playbackStopped();
|
||||
}
|
||||
}
|
||||
});
|
||||
audioClip.open(audioStream);
|
||||
applyVolume();
|
||||
return true;
|
||||
} else {
|
||||
LOG.warn("Sound source <%s> could not be played: No audio source found.", audioSource);
|
||||
}
|
||||
} catch( UnsupportedAudioFileException ex ) {
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: The specified audio file is not supported.", audioSource);
|
||||
} catch( LineUnavailableException ex ) {
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: Audio line for playing back is unavailable.", audioSource);
|
||||
} catch( IOException ex ) {
|
||||
LOG.warn(ex, "Sound source <%s> could not be played: Error playing the audio file.", audioSource);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void update( LineEvent event ) {
|
||||
LineEvent.Type type = event.getType();
|
||||
|
||||
if( type == LineEvent.Type.START ) {
|
||||
playing = true;
|
||||
} else if( type == LineEvent.Type.STOP ) {
|
||||
playing = false;
|
||||
if( disposeAfterPlay ) {
|
||||
this.dispose();
|
||||
disposeAfterPlay = false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
this.dispose();
|
||||
disposeAfterPlay = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Log LOG = Log.getLogger(Sound.class);
|
||||
|
||||
}
|
||||
@@ -169,7 +169,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
|
||||
* <code>super.copyFrom(shape)</code> sollten die Basiseigenschaften
|
||||
* {@code super.copyFrom(shape)} sollten die Basiseigenschaften
|
||||
* kopiert werden.
|
||||
*
|
||||
* @param shape
|
||||
@@ -222,7 +222,7 @@ public abstract class Shape extends FilledShape {
|
||||
* {@code buff} nach oben verschoben.
|
||||
*
|
||||
* @param shape
|
||||
* @param anchor
|
||||
* @param dir
|
||||
* @param buff
|
||||
*/
|
||||
public void moveTo( Shape shape, Options.Direction dir, double buff ) {
|
||||
@@ -47,7 +47,7 @@ public class ImageLoader {
|
||||
|
||||
/**
|
||||
* Lädt ein Bild von der angegebenen Quelle <var>source</var> und gibt das
|
||||
* Bild zurück oder <code>null</code>, 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.
|
||||
@@ -1,5 +1,11 @@
|
||||
package schule.ngb.zm.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Supplier;
|
||||
@@ -30,6 +36,10 @@ public final class Log {
|
||||
|
||||
private static final String ROOT_LOGGER = "schule.ngb.zm";
|
||||
|
||||
private static final String DEFAULT_LOG_FORMAT = "[%1$tT] [%4$11s] %5$s %6$s%n";
|
||||
|
||||
private static final String DEFAULT_DEBUG_FORMAT = "[%1$tT] [%4$11s] (%2$s) %5$s %6$s%n";
|
||||
|
||||
private static boolean LOGGING_INIT = false;
|
||||
|
||||
/**
|
||||
@@ -43,7 +53,7 @@ public final class Log {
|
||||
*
|
||||
* @see #enableGlobalLevel(Level)
|
||||
*/
|
||||
public static final void enableGlobalDebugging() {
|
||||
public static void enableGlobalDebugging() {
|
||||
enableGlobalLevel(ALL);
|
||||
}
|
||||
|
||||
@@ -68,32 +78,18 @@ public final class Log {
|
||||
* @param level Das Level, auf das alle {@code Logger} und {@code Handler}
|
||||
* mindestens herabgesenkt werden sollen.
|
||||
*/
|
||||
public static final void xenableGlobalLevel( Level level ) {
|
||||
public static void enableGlobalLevel( Level level ) {
|
||||
int lvl = Validator.requireNotNull(level).intValue();
|
||||
ensureRootLoggerIntialized();
|
||||
|
||||
Logger rootLogger = Logger.getLogger(ROOT_LOGGER);
|
||||
rootLogger.setLevel(level);
|
||||
|
||||
for( Handler handler : rootLogger.getHandlers() ) {
|
||||
if( handler instanceof ConsoleHandler ) {
|
||||
Level handlerLevel = handler.getLevel();
|
||||
if( handlerLevel == null || handler.getLevel().intValue() > lvl ) {
|
||||
handler.setLevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final void enableGlobalLevel( Level level ) {
|
||||
int lvl = Validator.requireNotNull(level).intValue();
|
||||
|
||||
// Decrease level of root level ConsoleHandlers for outpu
|
||||
// Decrease level of root level ConsoleHandlers for output
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
for( Handler handler : rootLogger.getHandlers() ) {
|
||||
if( handler instanceof ConsoleHandler ) {
|
||||
Level handlerLevel = handler.getLevel();
|
||||
if( handlerLevel == null || handler.getLevel().intValue() > lvl ) {
|
||||
handler.setLevel(level);
|
||||
handler.setFormatter(new LogFormatter());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,16 +113,42 @@ public final class Log {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger LOG = Logger.getLogger(ROOT_LOGGER);
|
||||
LOG.fine("Debug logging enabled.");
|
||||
}
|
||||
|
||||
public static final Log getLogger( Class<?> clazz ) {
|
||||
if( !LOGGING_INIT ) {
|
||||
Logger.getLogger(ROOT_LOGGER);
|
||||
LOGGING_INIT = true;
|
||||
}
|
||||
public static Log getLogger( Class<?> clazz ) {
|
||||
ensureRootLoggerIntialized();
|
||||
return new Log(clazz);
|
||||
}
|
||||
|
||||
private static void ensureRootLoggerIntialized() {
|
||||
if( LOGGING_INIT ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger rootLogger = Logger.getLogger(ROOT_LOGGER);
|
||||
rootLogger.setLevel(Level.INFO);
|
||||
|
||||
if( System.getProperty("java.util.logging.SimpleFormatter.format") == null
|
||||
&& LogManager.getLogManager().getProperty("java.util.logging.SimpleFormatter.format") == null ) {
|
||||
System.setProperty("java.util.logging.SimpleFormatter.format", DEFAULT_LOG_FORMAT);
|
||||
rootLogger.addHandler(new StreamHandler(System.err, new LogFormatter()));
|
||||
rootLogger.setUseParentHandlers(false);
|
||||
}
|
||||
if( rootLogger.getUseParentHandlers() ) {
|
||||
// This logger was not configured somewhere else
|
||||
// Add a Handler and Formatter
|
||||
|
||||
//StreamHandler rootHandler = new StreamHandler(System.out, new SimpleFormatter());
|
||||
//rootLogger.addHandler(rootHandler);
|
||||
//rootLogger.setUseParentHandlers(false);
|
||||
}
|
||||
|
||||
LOGGING_INIT = true;
|
||||
}
|
||||
|
||||
private final Logger LOGGER;
|
||||
|
||||
private final Class<?> sourceClass;
|
||||
@@ -315,4 +337,40 @@ public final class Log {
|
||||
this.log(FINER, throwable, msgSupplier);
|
||||
}
|
||||
|
||||
static final class LogFormatter extends Formatter {
|
||||
|
||||
@Override
|
||||
public String format( LogRecord record ) {
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(
|
||||
record.getInstant(), ZoneId.systemDefault());
|
||||
String source;
|
||||
if (record.getSourceClassName() != null) {
|
||||
source = record.getSourceClassName();
|
||||
if (record.getSourceMethodName() != null) {
|
||||
source += " " + record.getSourceMethodName();
|
||||
}
|
||||
} else {
|
||||
source = record.getLoggerName();
|
||||
}
|
||||
String message = formatMessage(record);
|
||||
String throwable = "";
|
||||
if (record.getThrown() != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
pw.println();
|
||||
record.getThrown().printStackTrace(pw);
|
||||
pw.close();
|
||||
throwable = sw.toString();
|
||||
}
|
||||
return String.format(DEFAULT_LOG_FORMAT,
|
||||
zdt,
|
||||
source,
|
||||
record.getLoggerName(),
|
||||
record.getLevel().getLocalizedName(),
|
||||
message,
|
||||
throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/main/resources/Icon.afdesign
Normal file
BIN
src/main/resources/Icon.afdesign
Normal file
Binary file not shown.
BIN
src/main/resources/icon_128.png
Normal file
BIN
src/main/resources/icon_128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
src/main/resources/icon_32.png
Normal file
BIN
src/main/resources/icon_32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/main/resources/icon_512.png
Normal file
BIN
src/main/resources/icon_512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
BIN
src/main/resources/icon_64.png
Normal file
BIN
src/main/resources/icon_64.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
227
src/test/java/schule/ngb/zm/ColorTest.java
Normal file
227
src/test/java/schule/ngb/zm/ColorTest.java
Normal file
@@ -0,0 +1,227 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ColorTest {
|
||||
|
||||
@Test
|
||||
public void init() {
|
||||
Color c;
|
||||
|
||||
c = new Color();
|
||||
assertEquals(0, c.getRed());
|
||||
assertEquals(0, c.getGreen());
|
||||
assertEquals(0, c.getBlue());
|
||||
assertEquals(255, c.getAlpha());
|
||||
|
||||
c = Color.BLUE;
|
||||
assertEquals(0, c.getRed());
|
||||
assertEquals(0, c.getGreen());
|
||||
assertEquals(255, c.getBlue());
|
||||
assertEquals(255, c.getAlpha());
|
||||
|
||||
c = new Color(50, 133, 64, 33);
|
||||
assertEquals(50, c.getRed());
|
||||
assertEquals(133, c.getGreen());
|
||||
assertEquals(64, c.getBlue());
|
||||
assertEquals(33, c.getAlpha());
|
||||
|
||||
c = new Color(255, 0, 0);
|
||||
assertEquals(Color.RED, c);
|
||||
|
||||
c = new Color(33, 50);
|
||||
assertEquals(33, c.getRed());
|
||||
assertEquals(33, c.getGreen());
|
||||
assertEquals(33, c.getBlue());
|
||||
assertEquals(50, c.getAlpha());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse() {
|
||||
Color c;
|
||||
|
||||
c = Color.getRGBColor(0x00FF00FF);
|
||||
assertEquals(0x00FF00FF, c.getRGBA());
|
||||
assertEquals(255, c.getRed());
|
||||
assertEquals(0, c.getGreen());
|
||||
assertEquals(255, c.getBlue());
|
||||
assertEquals(0, c.getAlpha());
|
||||
|
||||
c = Color.getRGBColor(0x33FF3333);
|
||||
assertEquals(0x33FF3333, c.getRGBA());
|
||||
assertEquals(255, c.getRed());
|
||||
assertEquals(51, c.getGreen());
|
||||
assertEquals(51, c.getBlue());
|
||||
assertEquals(51, c.getAlpha());
|
||||
|
||||
c = Color.parseHexcode("FF00FF");
|
||||
assertEquals(0xFFFF00FF, c.getRGBA());
|
||||
assertEquals(255, c.getRed());
|
||||
assertEquals(0, c.getGreen());
|
||||
assertEquals(255, c.getBlue());
|
||||
assertEquals(255, c.getAlpha());
|
||||
|
||||
c = Color.parseHexcode("#FF00FF00");
|
||||
assertEquals(0x00FF00FF, c.getRGBA());
|
||||
assertEquals(255, c.getRed());
|
||||
assertEquals(0, c.getGreen());
|
||||
assertEquals(255, c.getBlue());
|
||||
assertEquals(0, c.getAlpha());
|
||||
|
||||
c = Color.parseHexcode("#333");
|
||||
assertEquals(0xFF333333, c.getRGBA());
|
||||
assertEquals(51, c.getRed());
|
||||
assertEquals(51, c.getGreen());
|
||||
assertEquals(51, c.getBlue());
|
||||
assertEquals(255, c.getAlpha());
|
||||
|
||||
c = Color.parseHexcode("#33FF0033");
|
||||
assertEquals(0x3333FF00, c.getRGBA());
|
||||
assertEquals(51, c.getRed());
|
||||
assertEquals(255, c.getGreen());
|
||||
assertEquals(0, c.getBlue());
|
||||
assertEquals(51, c.getAlpha());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEquals() {
|
||||
Color c1 = new Color(254, 98, 12);
|
||||
Color c2 = new Color(254, 98, 12);
|
||||
assertNotSame(c1, c2);
|
||||
assertEquals(c1, c2);
|
||||
assertEquals(c1, c1);
|
||||
c2 = new Color(c1);
|
||||
assertNotSame(c1, c2);
|
||||
assertEquals(c1, c2);
|
||||
|
||||
Color yellow = new Color(255, 255, 0);
|
||||
assertEquals(java.awt.Color.YELLOW, yellow);
|
||||
assertNotEquals(java.awt.Color.YELLOW, Color.YELLOW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void construct() {
|
||||
Color c;
|
||||
// Empty color is white
|
||||
c = new Color();
|
||||
assertEquals(Color.BLACK, c);
|
||||
// One arg is shade of gray
|
||||
c = new Color(255);
|
||||
assertEquals(Color.WHITE, c);
|
||||
c = new Color(0);
|
||||
assertEquals(Color.BLACK, c);
|
||||
c = new Color(64);
|
||||
assertEquals(Color.DARKGRAY, c);
|
||||
c = new Color(192);
|
||||
assertEquals(Color.LIGHTGRAY, c);
|
||||
// RGB colors
|
||||
c = new Color(0,0,0);
|
||||
assertEquals(Color.BLACK, c);
|
||||
c = new Color(255,0,0);
|
||||
assertEquals(java.awt.Color.RED, c.getJavaColor());
|
||||
c = new Color(0,255,0);
|
||||
assertEquals(java.awt.Color.GREEN, c.getJavaColor());
|
||||
c = new Color(0,0,255);
|
||||
assertEquals(java.awt.Color.BLUE, c.getJavaColor());
|
||||
// From java.awt.Color
|
||||
c = new Color(java.awt.Color.YELLOW);
|
||||
assertEquals(java.awt.Color.YELLOW, c.getJavaColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRGBColor() {
|
||||
Color c1 = Color.getRGBColor(0xFFFF0000);
|
||||
assertEquals(Color.RED, c1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHSBColor() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHSLColor() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseHexcode() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void morph() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void RGBtoHSL() {
|
||||
Color c;
|
||||
float[] hsl;
|
||||
|
||||
c = Color.RED;
|
||||
hsl = Color.RGBtoHSL(c.getRGBA(), null);
|
||||
assertArrayEquals(new float[]{0f,1f,.5f}, hsl, 0.0001f);
|
||||
|
||||
c = new Color(255, 33, 64);
|
||||
hsl = Color.RGBtoHSL(c.getRGBA(), null);
|
||||
assertEquals(352, hsl[0], 1.0f);
|
||||
assertEquals(1.0f, hsl[1], .0001f);
|
||||
assertEquals(.5647f, hsl[2], .0001f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void HSLtoRGB() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHSLtoRGB() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void copy() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRGBA() {
|
||||
Color yellow = new Color(255, 255, 0);
|
||||
assertEquals(java.awt.Color.YELLOW.getRGB(), Color.YELLOW.getRGBA());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRed() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getGreen() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBlue() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlpha() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getJavaColor() {
|
||||
assertEquals(java.awt.Color.YELLOW, Color.YELLOW.getJavaColor());
|
||||
assertEquals(new java.awt.Color(255, 31, 124), new Color(255, 31, 124).getJavaColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void brighter() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBrighter() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void darker() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDarker() {
|
||||
}
|
||||
|
||||
}
|
||||
131
src/test/java/schule/ngb/zm/ConstantsTest.java
Normal file
131
src/test/java/schule/ngb/zm/ConstantsTest.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ConstantsTest {
|
||||
|
||||
@Test
|
||||
void min() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void max() {
|
||||
assertEquals(5.0, Constants.max(5.0, 0.0), 0.000001);
|
||||
assertEquals(5.0, Constants.max(5.0, 4.0), 0.000001);
|
||||
assertEquals(5.0, Constants.max(5.0, 5.0), 0.000001);
|
||||
assertEquals(100.0, Constants.max(100.0, -100.0), 0.000001);
|
||||
assertEquals(0.0, Constants.max(0.0, 0.0), 0.000001);
|
||||
|
||||
assertEquals(5.0, Constants.max(new double[]{ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 }), 0.000001);
|
||||
assertEquals(5.0, Constants.max(new double[]{ 5.0, 5.0, 5.0, 5.0, 5.0 }), 0.000001);
|
||||
assertEquals(5.0, Constants.max(new double[]{ 5.0 }), 0.000001);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> Constants.max(null));
|
||||
assertThrows(IllegalArgumentException.class, () -> Constants.max(new double[]{}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void abs() {
|
||||
assertEquals(5.0, Constants.abs(5.0), 0.000001);
|
||||
assertEquals(0.0, Constants.abs(0.0), 0.000001);
|
||||
assertEquals(105.0, Constants.abs(-105.0), 0.000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sign() {
|
||||
assertEquals(1.0, Constants.sign(5.0), 0.000001);
|
||||
assertEquals(0.0, Constants.sign(0.0), 0.000001);
|
||||
assertEquals(-1.0, Constants.sign(-105.0), 0.000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void morph() {
|
||||
assertEquals(0.0, Constants.interpolate(0.0, 100.0, 0.0), 0.000001);
|
||||
assertEquals(10.0, Constants.interpolate(0.0, 100.0, 0.1), 0.000001);
|
||||
assertEquals(50.0, Constants.interpolate(0.0, 100.0, 0.5), 0.000001);
|
||||
assertEquals(100.0, Constants.interpolate(0.0, 100.0, 1.0), 0.000001);
|
||||
assertEquals(-100.0, Constants.interpolate(-100.0, 100.0, 0.0), 0.000001);
|
||||
assertEquals(0.0, Constants.interpolate(-100.0, 100.0, 0.5), 0.000001);
|
||||
assertEquals(100.0, Constants.interpolate(-100.0, 100.0, 1.0), 0.000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void limit() {
|
||||
assertEquals(4, Constants.limit(4, 1, 5));
|
||||
assertEquals(1, Constants.limit(0, 1, 5));
|
||||
assertEquals(5, Constants.limit(6, 1, 5));
|
||||
assertEquals(1, Constants.limit(-6, 1, 5));
|
||||
assertEquals(-6, Constants.limit(-6, -10, 5));
|
||||
assertEquals(-10, Constants.limit(-15, -10, 5));
|
||||
assertEquals(-10, Constants.limit(-10, -10, 5));
|
||||
|
||||
assertEquals(4.0, Constants.limit(4, 1, 5), 0.000001);
|
||||
assertEquals(1.3, Constants.limit(0, 1.3, 5), 0.000001);
|
||||
assertEquals(5.0, Constants.limit(6, 1, 5), 0.000001);
|
||||
assertEquals(1.2, Constants.limit(-6, 1.2, 5), 0.000001);
|
||||
assertEquals(-6.0, Constants.limit(-6, -10, 5), 0.000001);
|
||||
assertEquals(-10.0, Constants.limit(-15, -10, 5), 0.000001);
|
||||
assertEquals(-10.3, Constants.limit(-10.3, -10.3, 5), 0.000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void b() {
|
||||
assertTrue(Constants.getBool(true));
|
||||
assertFalse(Constants.getBool(false));
|
||||
assertTrue(Constants.getBool(1));
|
||||
assertFalse(Constants.getBool(0));
|
||||
assertTrue(Constants.getBool(4.0));
|
||||
assertFalse(Constants.getBool(0.0));
|
||||
assertTrue(Constants.getBool(4.0f));
|
||||
assertFalse(Constants.getBool(0.0f));
|
||||
assertTrue(Constants.getBool(4L));
|
||||
assertFalse(Constants.getBool(0L));
|
||||
assertTrue(Constants.getBool("true"));
|
||||
assertTrue(Constants.getBool("True"));
|
||||
assertFalse(Constants.getBool("1"));
|
||||
assertFalse(Constants.getBool("false"));
|
||||
assertFalse(Constants.getBool("yes"));
|
||||
assertFalse(Constants.getBool("no"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void randomBool() {
|
||||
float t = 0f, f = 0f;
|
||||
for( int i = 0; i < 100000; i++ ) {
|
||||
if( Constants.randomBool() ) {
|
||||
t++;
|
||||
} else {
|
||||
f++;
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(.5f, Math.abs(t/(t+f)), .01f);
|
||||
|
||||
t = 0f;
|
||||
f = 0f;
|
||||
for( int i = 0; i < 100000; i++ ) {
|
||||
if( Constants.randomBool(.2) ) {
|
||||
t++;
|
||||
} else {
|
||||
f++;
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(.2f, Math.abs(t/(t+f)), .01f);
|
||||
|
||||
t = 0f;
|
||||
f = 0f;
|
||||
for( int i = 0; i < 100000; i++ ) {
|
||||
if( Constants.randomBool(80) ) {
|
||||
t++;
|
||||
} else {
|
||||
f++;
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(.8f, Math.abs(t/(t+f)), .01f);
|
||||
}
|
||||
|
||||
}
|
||||
58
src/test/java/schule/ngb/zm/OptionsTest.java
Normal file
58
src/test/java/schule/ngb/zm/OptionsTest.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import static schule.ngb.zm.Options.Direction.*;
|
||||
|
||||
class OptionsTest {
|
||||
|
||||
@Test
|
||||
void testDirections() {
|
||||
assertTrue(NORTH.in(NORTH));
|
||||
assertTrue(NORTH.in(NORTHEAST));
|
||||
assertTrue(EAST.in(NORTHEAST));
|
||||
assertTrue(NORTH.in(NORTHWEST));
|
||||
assertTrue(WEST.in(NORTHWEST));
|
||||
assertFalse(NORTH.in(SOUTH));
|
||||
assertFalse(NORTH.in(EAST));
|
||||
assertFalse(NORTH.in(WEST));
|
||||
assertFalse(NORTH.in(SOUTHWEST));
|
||||
assertFalse(NORTH.in(SOUTHEAST));
|
||||
|
||||
assertFalse(NORTHEAST.in(NORTH));
|
||||
assertFalse(NORTHEAST.in(SOUTH));
|
||||
assertFalse(NORTHEAST.in(NORTHWEST));
|
||||
assertTrue(NORTHEAST.in(NORTHEAST));
|
||||
|
||||
assertTrue(SOUTH.contains(SOUTH));
|
||||
assertFalse(SOUTH.contains(NORTH));
|
||||
assertFalse(SOUTH.contains(EAST));
|
||||
assertFalse(SOUTH.contains(WEST));
|
||||
assertFalse(SOUTH.contains(NORTHWEST));
|
||||
assertFalse(SOUTH.contains(SOUTHWEST));
|
||||
assertFalse(SOUTH.contains(SOUTHEAST));
|
||||
|
||||
assertTrue(SOUTHEAST.contains(SOUTH));
|
||||
assertTrue(SOUTHEAST.contains(EAST));
|
||||
assertTrue(SOUTHWEST.contains(SOUTH));
|
||||
assertTrue(SOUTHWEST.contains(WEST));
|
||||
assertFalse(SOUTHWEST.contains(NORTH));
|
||||
assertFalse(SOUTHWEST.contains(SOUTHEAST));
|
||||
assertTrue(SOUTHWEST.contains(SOUTHWEST));
|
||||
assertFalse(SOUTHWEST.contains(EAST));
|
||||
assertFalse(SOUTHWEST.contains(NORTHWEST));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOppositeDirection() {
|
||||
assertEquals(SOUTH, NORTH.inverse());
|
||||
assertEquals(NORTH, SOUTH.inverse());
|
||||
assertEquals(WEST, EAST.inverse());
|
||||
assertEquals(EAST, WEST.inverse());
|
||||
|
||||
assertEquals(SOUTHEAST, NORTHWEST.inverse());
|
||||
}
|
||||
|
||||
}
|
||||
114
src/test/java/schule/ngb/zm/TestAttraction.java
Normal file
114
src/test/java/schule/ngb/zm/TestAttraction.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import schule.ngb.zm.shapes.Rectangle;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public class TestAttraction extends Zeichenmaschine {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new TestAttraction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settings() {
|
||||
setSize(800, 600);
|
||||
setTitle("My test Window");
|
||||
//setFramesPerSecond(5);
|
||||
|
||||
s2dl = new Shape2DLayer();
|
||||
addLayer(s2dl);
|
||||
}
|
||||
|
||||
Shape2DLayer s2dl;
|
||||
|
||||
Vector posA, posB, velB, posC, velC;
|
||||
|
||||
double massA = 500, massB = 1, massC = 4.3, G = 5.0;
|
||||
|
||||
Rectangle recht;
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
//zeichnung.hide();
|
||||
drawing.clear(200);
|
||||
posA = new Vector(0, 0);
|
||||
posB = new Vector(-100, -100);
|
||||
velB = new Vector(10, -10);
|
||||
posC = new Vector(200, 100);
|
||||
velC = new Vector(1, 14);
|
||||
|
||||
drawing.translate(width /2, height /2);
|
||||
drawing.shear(0.1, 0.5);
|
||||
|
||||
recht = new Rectangle(50, 50, 150, 75);
|
||||
recht.setFillColor(200);
|
||||
recht.setStrokeColor(255, 0, 64);
|
||||
recht.setStrokeWeight(2.5);
|
||||
recht.setStrokeType(DASHED);
|
||||
shapes.add(recht);
|
||||
|
||||
drawing.hide();
|
||||
//schule.ngb.zm.formen.verstecken();
|
||||
|
||||
s2dl.setFillColor(64,200,128);
|
||||
s2dl.add(new Rectangle2D.Double(100, 100, 120, 80));
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
drawing.setStrokeColor(255);
|
||||
drawing.setStrokeWeight(4.0);
|
||||
drawing.setStrokeType(DASHED);
|
||||
drawing.clear(33, 33, 33, 100);
|
||||
|
||||
drawing.setFillColor(Color.ORANGE);
|
||||
drawing.pie(posA.x, posA.y, 80, 30, 60);
|
||||
drawing.setFillColor(Color.YELLOW);
|
||||
drawing.circle(posA.x, posA.y, 60);
|
||||
|
||||
Vector acc = acceleration(posA, posB, massA, massB);
|
||||
velB.add(acc);
|
||||
posB.add(velB);
|
||||
|
||||
drawing.setFillColor(Color.BLUE);
|
||||
drawing.circle(posB.x, posB.y, 20);
|
||||
|
||||
acc = acceleration(posA, posC, massA, massC);
|
||||
velC.add(acc);
|
||||
posC.add(velC);
|
||||
|
||||
drawing.setFillColor(Color.GREEN);
|
||||
drawing.circle(posC.x, posC.y, 20);
|
||||
|
||||
drawing.rotate(1);
|
||||
|
||||
shapes.clear();
|
||||
double x = recht.getX();
|
||||
x = (x+100*delta)% width;
|
||||
recht.setX(x);
|
||||
}
|
||||
|
||||
Vector acceleration( Vector a, Vector b, double ma, double mb ) {
|
||||
Vector acc = Vector.sub(a, b);
|
||||
double draw = (G*ma*mb)/acc.lengthSq();
|
||||
acc.setLength(draw*delta);
|
||||
acc.limit(3, 30);
|
||||
return acc;
|
||||
}
|
||||
|
||||
public void mouseDragged() {
|
||||
drawing.translate(mouseX - pmouseX, mouseY - pmouseY);
|
||||
}
|
||||
|
||||
boolean zoom = true;
|
||||
public void mouseClicked() {
|
||||
//canvas.translateToCanvas(mouseX-width/2.0, mouseY-height/2.0);
|
||||
if( zoom ) {
|
||||
drawing.scale(2.0);
|
||||
} else {
|
||||
drawing.scale(.5);
|
||||
}
|
||||
zoom = !zoom;
|
||||
}
|
||||
|
||||
}
|
||||
71
src/test/java/schule/ngb/zm/TestDrawing.java
Normal file
71
src/test/java/schule/ngb/zm/TestDrawing.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
public class TestDrawing extends Zeichenmaschine {
|
||||
|
||||
private final double w = 100, h = 50;
|
||||
|
||||
private double xa = 50, ya = 50;
|
||||
|
||||
private double x2 = 200, y2 = 50;
|
||||
|
||||
int frames = 0;
|
||||
long start = 0;
|
||||
|
||||
public static void main( String[] args ) {
|
||||
new TestDrawing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
drawing.setFont("AvenirNext-Medium", 14);
|
||||
drawing.setAnchor(NORTHWEST);
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( double delta ) {
|
||||
ya = (ya+1)%350;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
long ms = System.currentTimeMillis();
|
||||
|
||||
drawing.clear();
|
||||
|
||||
// x2 = mouseX;
|
||||
// y2 = mouseY;
|
||||
|
||||
drawing.resetStroke();
|
||||
drawing.rect(xa, ya, w, h);
|
||||
|
||||
//drawing.image("WitchCraftIcons_122_t.PNG", width/2, height/2, .5, CENTER);
|
||||
|
||||
for( int i = 0; i < 4; i++ ) {
|
||||
//delay(1000);
|
||||
drawing.resetStroke();
|
||||
drawing.rect(x2 + i*10, y2 + i * 75, w, h);
|
||||
connect(xa + w, ya + h / 2.0, x2 + i*10, y2 + i * 75 + h / 2.0);
|
||||
drawing.text("Rect " + i, x2 + i*10 + w / 2.0, y2 + i * 75 + h / 2.0, CENTER);
|
||||
}
|
||||
|
||||
|
||||
if( (System.currentTimeMillis()-start) >= 1000 ) {
|
||||
System.out.printf("FPS: %d\n", frames);
|
||||
frames = 0;
|
||||
start = System.currentTimeMillis();
|
||||
} else {
|
||||
frames += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void connect( double x1, double y1, double x2, double y2 ) {
|
||||
double midx = (x1 + x2) / 2.0;
|
||||
drawing.setStrokeType(DASHED);
|
||||
drawing.setStrokeWeight(2.4);
|
||||
drawing.curve(x1, y1, midx, y1, midx, y2, x2, y2);
|
||||
//drawing.curve(x1, y1, x2, y1, x1, y2, x2, y2);
|
||||
}
|
||||
|
||||
}
|
||||
46
src/test/java/schule/ngb/zm/TestInput.java
Normal file
46
src/test/java/schule/ngb/zm/TestInput.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import schule.ngb.zm.shapes.*;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
public class TestInput extends Zeichenmaschine {
|
||||
|
||||
public static void main( String[] args ) {
|
||||
new TestInput();
|
||||
}
|
||||
|
||||
private Text text;
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
|
||||
//setFramesPerSecond(5);
|
||||
shapes.add(new Point(200, 200));
|
||||
shapes.add(new Point(200, 200));
|
||||
|
||||
shapes.getShape(0).setFillColor(GREEN);
|
||||
shapes.getShape(1).setFillColor(PINK);
|
||||
|
||||
text = new Text(10, 42, "");
|
||||
text.setFontsize(32);
|
||||
text.setAnchor(NORTHWEST);
|
||||
shapes.add(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( double delta ) {
|
||||
shapes.getShape(0).moveTo(mouseX, mouseY);
|
||||
shapes.getShape(1).moveTo(pmouseX, pmouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
drawing.line(pmouseX, pmouseY, mouseX, mouseY);
|
||||
}
|
||||
|
||||
public void keyPressed() {
|
||||
text.setText(""+key);
|
||||
}
|
||||
|
||||
}
|
||||
130
src/test/java/schule/ngb/zm/TestShapes.java
Normal file
130
src/test/java/schule/ngb/zm/TestShapes.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import schule.ngb.zm.shapes.*;
|
||||
import schule.ngb.zm.shapes.Point;
|
||||
import schule.ngb.zm.shapes.Polygon;
|
||||
import schule.ngb.zm.shapes.Rectangle;
|
||||
import schule.ngb.zm.shapes.Shape;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.Random;
|
||||
|
||||
public class TestShapes extends Zeichenmaschine {
|
||||
|
||||
public static void main( String[] args ) {
|
||||
new TestShapes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
setSize(400, 400);
|
||||
|
||||
//createShapes();
|
||||
|
||||
//add(new Triangle(200, 150, 150, 250, 250, 250));
|
||||
//add(new Rectangle(200, 200, 200, 100));
|
||||
//shapes.getShape(0).setAnchor(CENTER);
|
||||
|
||||
shapePositions();
|
||||
}
|
||||
|
||||
public void createShapes() {
|
||||
add(new Rectangle(20, 10, 40, 20));
|
||||
add(new Ellipse(40, 50, 40, 20));
|
||||
add(new Circle(40, 80, 10));
|
||||
|
||||
add(new Line(40, 100, 100, 40));
|
||||
add(new Arrow(200, 200, 105, 45));
|
||||
|
||||
shapes.add(new Point(200, 200));
|
||||
|
||||
add(new Rhombus(40, 200, 50, 120));
|
||||
add(new Kite(40, 200, 50, 120, .75));
|
||||
add(new Kite(40, 200, 50, 120, .25));
|
||||
|
||||
add(new RoundedRectangle(20, 300, 100, 60, 30));
|
||||
|
||||
add(new Arc(200, 200, 60, 90, 120));
|
||||
|
||||
add(new Polygon(250, 40, 300, 55, 321, 83, 200, 300));
|
||||
shapes.add(new Point(250, 40));
|
||||
shapes.add(new Point(300, 55));
|
||||
shapes.add(new Point(321, 83));
|
||||
shapes.add(new Point(200, 300));
|
||||
|
||||
add(new Triangle(300, 55, 355, 75, 345, 25));
|
||||
add(new Quad(300, 55, 355, 75, 345, 25, 300, 10));
|
||||
|
||||
add(new Curve(50, 50, 350, 50, 350, 350));
|
||||
add(new Curve(50, 50, 50, 350, 350, 50, 350, 350));
|
||||
|
||||
shapes.add(new Picture(300, 300, "WitchCraftIcons_122_t.PNG"));
|
||||
|
||||
add(new Text(200, 40, "Shapes 😊"));
|
||||
}
|
||||
|
||||
public void shapePositions() {
|
||||
int pad = 24;
|
||||
Rectangle bounds = new Rectangle(pad, pad, width-pad, height-pad);
|
||||
|
||||
Rectangle[] rects = new Rectangle[5];
|
||||
for( int i = 0; i < rects.length; i++ ) {
|
||||
rects[i] = new Rectangle(40 + i*15, 40 + i*15, 10, 10+i*4);
|
||||
}
|
||||
shapes.add(rects);
|
||||
|
||||
for( Rectangle r: rects ) {
|
||||
r.alignTo(bounds, LEFT);
|
||||
}
|
||||
|
||||
//Text t = new TextBox(width/2, height/2, 200, 200, "Hello,\nWorld!");
|
||||
//shapes.add(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( double delta ) {
|
||||
//shapes.getShape(Triangle.class).rotate(2);
|
||||
//shapes.getShape(0).rotate(200, 250,2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked() {
|
||||
for( Shape s : shapes.getShapes() ) {
|
||||
randomizeShape(s);
|
||||
//s.move(10, 10);
|
||||
}
|
||||
}
|
||||
|
||||
private void add( Shape s ) {
|
||||
shapes.add(randomizeShape(s));
|
||||
}
|
||||
|
||||
private Shape randomizeShape( Shape s ) {
|
||||
if( !(s instanceof Arc) && !(s instanceof Curve) && !(s instanceof Line) && !(s instanceof Arrow) ) {
|
||||
s.setFillColor(randomColor());
|
||||
}
|
||||
s.setStrokeColor(randomColor());
|
||||
s.setStrokeWeight(random(.75, 2.25));
|
||||
if( randomBool(20) ) {
|
||||
s.setStrokeType(DASHED);
|
||||
} else {
|
||||
s.setStrokeType(SOLID);
|
||||
}
|
||||
|
||||
//s.moveTo(random(20, 380), random(20, 380));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private void showDot( Point2D p ) {
|
||||
showDot(p.getX(), p.getY(), randomColor());
|
||||
}
|
||||
|
||||
private void showDot( double x, double y, Color clr ) {
|
||||
Point p = new Point(x, y);
|
||||
p.setFillColor(clr);
|
||||
p.setStrokeColor(clr);
|
||||
shapes.add(p);
|
||||
}
|
||||
|
||||
}
|
||||
78
src/test/java/schule/ngb/zm/TestTurtle.java
Normal file
78
src/test/java/schule/ngb/zm/TestTurtle.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import schule.ngb.zm.turtle.TurtleLayer;
|
||||
import schule.ngb.zm.turtle.TurtleLayer.Turtle;
|
||||
|
||||
public class TestTurtle extends Zeichenmaschine {
|
||||
|
||||
public static void main( String[] args ) {
|
||||
new TestTurtle();
|
||||
}
|
||||
|
||||
TurtleLayer turtle;
|
||||
|
||||
TurtleLayer.Turtle t1, t2;
|
||||
|
||||
public void setup() {
|
||||
turtle = new TurtleLayer();
|
||||
addLayer(turtle);
|
||||
|
||||
t1 = turtle.getTurtle();
|
||||
t2 = turtle.newTurtle();
|
||||
|
||||
t1.setStrokeWeight(2.0);
|
||||
t2.setStrokeWeight(1.4);
|
||||
|
||||
t1.setFillColor(244, 64, 22);
|
||||
t2.setFillColor(33, 64, 255);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( double delta ) {
|
||||
//t1.right(1);
|
||||
t1.penUp();
|
||||
t1.moveTo(100, 100);
|
||||
t1.penDown();
|
||||
|
||||
t2.penUp();
|
||||
t2.moveTo(200, 200);
|
||||
t2.penDown();
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
turtle.clear();
|
||||
star(5, 80, 40, t1);
|
||||
star(5, 80, 40, t2);
|
||||
}
|
||||
|
||||
void star( int corners, int size ) {
|
||||
star(5, size, 40, t1);
|
||||
}
|
||||
|
||||
void star( int corners, int size, int angle, Turtle t ) {
|
||||
int angle2 = (180 - (360/corners) - angle) / 2;
|
||||
|
||||
t.penUp();
|
||||
t.left();
|
||||
t.fd(size*1.25);
|
||||
t.right();
|
||||
t.penDown();
|
||||
|
||||
for( int i = 0; i < corners; i++ ) {
|
||||
t.right(360/corners);
|
||||
t.right(angle2);
|
||||
t.fd(size);
|
||||
t.left(2*angle2);
|
||||
t.fd(size);
|
||||
t.right(angle2);
|
||||
}
|
||||
t.fill();
|
||||
|
||||
t.penUp();
|
||||
t.right();
|
||||
t.fd(size*1.25);
|
||||
t.left();
|
||||
t.penDown();
|
||||
}
|
||||
|
||||
}
|
||||
261
src/test/java/schule/ngb/zm/VectorTest.java
Normal file
261
src/test/java/schule/ngb/zm/VectorTest.java
Normal file
@@ -0,0 +1,261 @@
|
||||
package schule.ngb.zm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class VectorTest {
|
||||
|
||||
@Test
|
||||
public void init() {
|
||||
Vector vec;
|
||||
|
||||
vec = new Vector();
|
||||
assertEquals(0.0, vec.x, 0.0);
|
||||
assertEquals(0.0, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(4.5, 5.1);
|
||||
assertEquals(4.5, vec.x, 0.0);
|
||||
assertEquals(5.1, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(vec);
|
||||
assertEquals(4.5, vec.x, 0.0);
|
||||
assertEquals(5.1, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(Double.MAX_VALUE, Double.MIN_VALUE);
|
||||
assertEquals(Double.MAX_VALUE, vec.x, 0.0);
|
||||
assertEquals(Double.MIN_VALUE, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(new Point2D.Double(7.2, 3.5677));
|
||||
assertEquals(7.2, vec.x, 0.0);
|
||||
assertEquals(3.5677, vec.y, 0.0);
|
||||
|
||||
vec = new Vector(new Point2D.Float(7.2f, 3.5677f));
|
||||
assertEquals(7.2, vec.x, 0.0001);
|
||||
assertEquals(3.5677, vec.y, 0.0001);
|
||||
|
||||
vec.set(new Vector(5.1, 8.9));
|
||||
assertEquals(5.1, vec.x, 0.0001);
|
||||
assertEquals(8.9, vec.y, 0.0001);
|
||||
|
||||
Vector vec2 = vec.copy();
|
||||
assertNotSame(vec, vec2);
|
||||
assertEquals(vec, vec2);
|
||||
assertEquals(5.1, vec2.x, 0.0001);
|
||||
assertEquals(8.9, vec2.y, 0.0001);
|
||||
|
||||
assertEquals(vec, vec.set(100, 100));
|
||||
assertEquals(200, vec.set(200, 200).x, 0.0001);
|
||||
assertEquals(8.9, vec.set(vec2).y, 0.0001);
|
||||
|
||||
for( int i = 0; i < 50; i++ ) {
|
||||
vec = Vector.random();
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= 0);
|
||||
assertTrue(vec.x < 100);
|
||||
assertTrue(vec.y < 100);
|
||||
}
|
||||
|
||||
for( int i = 1; i < 50; i++ ) {
|
||||
vec = Vector.random(0, i*10);
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= 0);
|
||||
assertTrue(vec.x < i*10);
|
||||
assertTrue(vec.y < i*10);
|
||||
}
|
||||
|
||||
for( int i = 1; i < 50; i++ ) {
|
||||
vec = Vector.random(0, i*10, (i+1)*10, (i+2)*10);
|
||||
assertTrue(vec.x >= 0);
|
||||
assertTrue(vec.y >= (i+1)*10);
|
||||
assertTrue(vec.x < i*10);
|
||||
assertTrue(vec.y < (i+2)*10);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void setLength() {
|
||||
Vector vec = new Vector(2.0, 5.4);
|
||||
Vector vec2 = Vector.setLength(vec, 10);
|
||||
assertEquals(10.0, vec2.length(), 0.00001);
|
||||
Vector vec3 = Vector.setLength(vec, -10);
|
||||
assertEquals(10.0, vec3.length(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalize() {
|
||||
Vector vec = new Vector(4.0, 3.0);
|
||||
assertEquals(5.0, vec.length(), 0.00001);
|
||||
Vector vec2 = vec.normalize();
|
||||
assertSame(vec, vec2);
|
||||
assertEquals(1.0, vec.length(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void add() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void sub() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void scale() {
|
||||
Vector vec = new Vector(4.0, 3.0);
|
||||
assertEquals(5.0, vec.length(), 0.00001);
|
||||
Vector vec2 = vec.scale(10.0);
|
||||
assertSame(vec, vec2);
|
||||
assertEquals(50.0, vec.length(), 0.00001);
|
||||
vec2.scale(-10.0);
|
||||
assertEquals(500.0, vec.length(), 0.00001);
|
||||
assertEquals(-400.0, vec.x, 0.00001);
|
||||
assertEquals(-300.0, vec.y, 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void div() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void dist() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void dot() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void cross() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void rotate() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void morph() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void copy() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void set() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSet() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSet1() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void setX() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void setY() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPunkt() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void len() {
|
||||
Vector vec = new Vector(4.0, 3.0);
|
||||
assertEquals(5.0, vec.length(), 0.00001);
|
||||
vec = new Vector(1.0, 0.0);
|
||||
assertEquals(1.0, vec.length(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lenSq() {
|
||||
Vector vec = new Vector(4.0, 3.0);
|
||||
assertEquals(25.0, vec.lengthSq(), 0.00001);
|
||||
vec = new Vector(1.0, 0.0);
|
||||
assertEquals(1.0, vec.lengthSq(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setLen() {
|
||||
Vector vec = new Vector(2.0, 5.4);
|
||||
Vector vec2 = vec.setLength(10);
|
||||
assertEquals(10.0, vec.length(), 0.00001);
|
||||
assertSame(vec, vec2);
|
||||
vec.setLength(-10);
|
||||
assertEquals(10.0, vec.length(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNormalize() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdd() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdd1() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSub() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSub1() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testScale() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDiv() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDist() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDot() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDot1() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCross() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void limit() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLimit() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void angle() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRotate() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMorph() {
|
||||
}
|
||||
|
||||
}
|
||||
202
src/test/java/schule/ngb/zm/shapes/ShapeTest.java
Normal file
202
src/test/java/schule/ngb/zm/shapes/ShapeTest.java
Normal file
@@ -0,0 +1,202 @@
|
||||
package schule.ngb.zm.shapes;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import schule.ngb.zm.Options;
|
||||
|
||||
import static schule.ngb.zm.Options.Direction.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
|
||||
class ShapeTest {
|
||||
|
||||
class TestShape extends Shape {
|
||||
|
||||
public TestShape() {
|
||||
super(42, 131);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWidth() {
|
||||
return 182;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHeight() {
|
||||
return 235;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape copy() {
|
||||
return new TestShape();
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.awt.Shape getShape() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShape() {
|
||||
Shape s = new TestShape();
|
||||
|
||||
assertEquals(42, s.getX());
|
||||
assertEquals(131, s.getY());
|
||||
assertEquals(182, s.getWidth());
|
||||
assertEquals(235, s.getHeight());
|
||||
|
||||
assertEquals(Options.Direction.CENTER, s.getAnchor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnchorPoint() {
|
||||
Shape s = new TestShape();
|
||||
|
||||
assertEquals(0.0, s.getAnchorPoint(CENTER).getX(), 0.00001);
|
||||
assertEquals(0.0, s.getAnchorPoint(CENTER).getY(), 0.00001);
|
||||
|
||||
assertEquals(0.0, s.getAnchorPoint(UP).getX(), 0.00001);
|
||||
assertEquals(-117.5, s.getAnchorPoint(UP).getY(), 0.00001);
|
||||
|
||||
assertEquals(-91.0, s.getAnchorPoint(LEFT).getX(), 0.00001);
|
||||
assertEquals(0.0, s.getAnchorPoint(LEFT).getY(), 0.00001);
|
||||
|
||||
assertEquals(91.0, s.getAnchorPoint(SOUTHEAST).getX(), 0.00001);
|
||||
assertEquals(117.5, s.getAnchorPoint(SOUTHEAST).getY(), 0.00001);
|
||||
|
||||
s.setAnchor(DOWN);
|
||||
|
||||
assertEquals(0.0, s.getAnchorPoint(DOWN).getX(), 0.00001);
|
||||
assertEquals(0.0, s.getAnchorPoint(DOWN).getY(), 0.00001);
|
||||
|
||||
assertEquals(0.0, s.getAnchorPoint(CENTER).getX(), 0.00001);
|
||||
assertEquals(-117.5, s.getAnchorPoint(CENTER).getY(), 0.00001);
|
||||
|
||||
assertEquals(0.0, s.getAnchorPoint(UP).getX(), 0.00001);
|
||||
assertEquals(-235.0, s.getAnchorPoint(UP).getY(), 0.00001);
|
||||
|
||||
assertEquals(-91.0, s.getAnchorPoint(LEFT).getX(), 0.00001);
|
||||
assertEquals(-117.5, s.getAnchorPoint(LEFT).getY(), 0.00001);
|
||||
|
||||
assertEquals(91.0, s.getAnchorPoint(SOUTHEAST).getX(), 0.00001);
|
||||
assertEquals(0-0, s.getAnchorPoint(SOUTHEAST).getY(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAbsAnchorPoint() {
|
||||
Shape s = new TestShape();
|
||||
|
||||
assertEquals(42.0, s.getAbsAnchorPoint(CENTER).getX(), 0.00001);
|
||||
assertEquals(131.0, s.getAbsAnchorPoint(CENTER).getY(), 0.00001);
|
||||
|
||||
assertEquals(42.0, s.getAbsAnchorPoint(UP).getX(), 0.00001);
|
||||
assertEquals(13.5, s.getAbsAnchorPoint(UP).getY(), 0.00001);
|
||||
|
||||
assertEquals(-49.0, s.getAbsAnchorPoint(LEFT).getX(), 0.00001);
|
||||
assertEquals(131.0, s.getAbsAnchorPoint(LEFT).getY(), 0.00001);
|
||||
|
||||
assertEquals(133.0, s.getAbsAnchorPoint(SOUTHEAST).getX(), 0.00001);
|
||||
assertEquals(248.5, s.getAbsAnchorPoint(SOUTHEAST).getY(), 0.00001);
|
||||
|
||||
s.setAnchor(DOWN);
|
||||
|
||||
assertEquals(42.0, s.getAbsAnchorPoint(CENTER).getX(), 0.00001);
|
||||
assertEquals(13.5, s.getAbsAnchorPoint(CENTER).getY(), 0.00001);
|
||||
|
||||
assertEquals(42.0, s.getAbsAnchorPoint(UP).getX(), 0.00001);
|
||||
assertEquals(-104.0, s.getAbsAnchorPoint(UP).getY(), 0.00001);
|
||||
|
||||
assertEquals(-49.0, s.getAbsAnchorPoint(LEFT).getX(), 0.00001);
|
||||
assertEquals(13.5, s.getAbsAnchorPoint(LEFT).getY(), 0.00001);
|
||||
|
||||
assertEquals(133.0, s.getAbsAnchorPoint(SOUTHEAST).getX(), 0.00001);
|
||||
assertEquals(131.0, s.getAbsAnchorPoint(SOUTHEAST).getY(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMoveTo() {
|
||||
Shape s1 = new TestShape();
|
||||
Shape s2 = s1.copy();
|
||||
|
||||
s1.moveTo( 100, 200);
|
||||
assertEquals(100.0, s1.x, 0.00001);
|
||||
assertEquals(200.0, s1.y, 0.00001);
|
||||
|
||||
s2.moveTo(s1);
|
||||
assertEquals(100.0, s2.x, 0.00001);
|
||||
assertEquals(200.0, s2.y, 0.00001);
|
||||
|
||||
s2.moveTo(s1, RIGHT);
|
||||
assertEquals(191.0, s2.x, 0.00001);
|
||||
assertEquals(200.0, s2.y, 0.00001);
|
||||
assertEquals(s2.getAbsAnchorPoint(CENTER), s1.getAbsAnchorPoint(RIGHT));
|
||||
assertEquals(s1.getAbsAnchorPoint(CENTER), s2.getAbsAnchorPoint(LEFT));
|
||||
|
||||
s2.moveTo(s1, RIGHT, 10.0);
|
||||
assertEquals(201.0, s2.x, 0.00001);
|
||||
assertEquals(200.0, s2.y, 0.00001);
|
||||
|
||||
s2.moveTo(s1, LEFT);
|
||||
assertEquals(9.0, s2.x, 0.00001);
|
||||
assertEquals(200.0, s2.y, 0.00001);
|
||||
|
||||
s2.moveTo(s1, LEFT, 10.0);
|
||||
assertEquals(-1.0, s2.x, 0.00001);
|
||||
assertEquals(200.0, s2.y, 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAlignTo() {
|
||||
Shape s1 = new TestShape();
|
||||
Shape s2 = new TestShape();
|
||||
|
||||
s1.moveTo(100, 200);
|
||||
|
||||
s2.alignTo(s1, LEFT, 0.0);
|
||||
assertEquals(100.0, s2.getX());
|
||||
assertEquals(131.0, s2.getY());
|
||||
assertEquals(9.0, s2.getAbsAnchorPoint(LEFT).getX(), 0.00001);
|
||||
|
||||
s2.moveTo(300, 400);
|
||||
s2.alignTo(s1, LEFT, 10.0);
|
||||
assertEquals(90.0, s2.getX());
|
||||
assertEquals(400.0, s2.getY());
|
||||
|
||||
s2.alignTo(s1, SOUTHEAST, 0.0);
|
||||
assertEquals(100.0, s2.getX());
|
||||
assertEquals(200.0, s2.getY());
|
||||
|
||||
s2.moveTo(0, 0);
|
||||
s2.alignTo(s1, DOWN, 24.0);
|
||||
assertEquals(0.0, s2.getX());
|
||||
assertEquals(224.0, s2.getY());
|
||||
|
||||
s2.setAnchor(NORTHWEST);
|
||||
s2.moveTo(0, 0);
|
||||
s2.alignTo(s1, DOWN, 24.0);
|
||||
assertEquals(0.0, s2.getX());
|
||||
assertEquals(106.5, s2.getY());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNextTo() {
|
||||
Shape s1 = new TestShape();
|
||||
Shape s2 = new TestShape();
|
||||
|
||||
s2.nextTo(s1, RIGHT, 10.0);
|
||||
assertEquals(234.0, s2.getX());
|
||||
assertEquals(131.0, s2.getY());
|
||||
|
||||
s2.nextTo(s1, LEFT, 10.0);
|
||||
assertEquals(-150.0, s2.getX());
|
||||
assertEquals(131.0, s2.getY());
|
||||
|
||||
s2.nextTo(s1, SOUTHEAST, 10.0);
|
||||
assertEquals(234.0, s2.getX());
|
||||
assertEquals(376.0, s2.getY());
|
||||
}
|
||||
|
||||
}
|
||||
127
src/test/java/schule/ngb/zm/util/ValidatorTest.java
Normal file
127
src/test/java/schule/ngb/zm/util/ValidatorTest.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package schule.ngb.zm.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ValidatorTest {
|
||||
|
||||
@Test
|
||||
void requireNotNull() {
|
||||
StringBuilder sb = new StringBuilder("Message");
|
||||
|
||||
Object o1 = new Object();
|
||||
assertEquals(o1, Validator.requireNotNull(o1));
|
||||
assertEquals(o1, Validator.requireNotNull(o1, "Message"));
|
||||
assertEquals(o1, Validator.requireNotNull(o1, sb));
|
||||
assertEquals(o1, Validator.requireNotNull(o1, ()->"Message"));
|
||||
|
||||
String o2 = null;
|
||||
assertThrowsExactly(NullPointerException.class, () -> Validator.requireNotNull(o2));
|
||||
assertThrowsExactly(NullPointerException.class, () -> Validator.requireNotNull(o2, "Message"));
|
||||
assertThrowsExactly(NullPointerException.class, () -> Validator.requireNotNull(o2, ()->"Message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void requireNotEmpty() {
|
||||
StringBuilder sb = new StringBuilder("Message");
|
||||
|
||||
String s1 = "Content";
|
||||
assertEquals(s1, Validator.requireNotEmpty(s1));
|
||||
assertEquals(s1, Validator.requireNotEmpty(s1, "Message"));
|
||||
assertEquals(s1, Validator.requireNotEmpty(s1, sb));
|
||||
assertEquals(s1, Validator.requireNotEmpty(s1, ()->"Message"));
|
||||
|
||||
String s2 = "";
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotEmpty(s2));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotEmpty(s2, "Message"));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotEmpty(s2, ()->"Message"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void requirePositive() {
|
||||
int i = 1;
|
||||
assertEquals(i, Validator.requirePositive(i));
|
||||
assertEquals(i, Validator.requirePositive(i, "Message"));
|
||||
assertEquals(i, Validator.requirePositive(i, ()->"Message"));
|
||||
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requirePositive(i-1));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requirePositive(i-1, "Message"));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requirePositive(i-1, ()->"Message"));
|
||||
|
||||
double k = 1.0;
|
||||
assertEquals(k, Validator.requirePositive(i));
|
||||
assertEquals(k, Validator.requirePositive(i, "Message"));
|
||||
assertEquals(k, Validator.requirePositive(i, ()->"Message"));
|
||||
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requirePositive(k-1.0));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requirePositive(k-1.0, "Message"));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requirePositive(k-1.0, ()->"Message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void requireNotNegative() {
|
||||
int i = 0;
|
||||
assertEquals(i, Validator.requireNotNegative(i));
|
||||
assertEquals(i, Validator.requireNotNegative(i, "Message"));
|
||||
assertEquals(i, Validator.requireNotNegative(i, ()->"Message"));
|
||||
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotNegative(i-1));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotNegative(i-1, "Message"));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotNegative(i-1, ()->"Message"));
|
||||
|
||||
double k = 0.0;
|
||||
assertEquals(k, Validator.requireNotNegative(i));
|
||||
assertEquals(k, Validator.requireNotNegative(i, "Message"));
|
||||
assertEquals(k, Validator.requireNotNegative(i, ()->"Message"));
|
||||
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotNegative(k-1.0));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotNegative(k-1.0, "Message"));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireNotNegative(k-1.0, ()->"Message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void requireInRange() {
|
||||
int min = 50, max = 100;
|
||||
for( int ii = min; ii < max; ii++ ) {
|
||||
final int i = ii;
|
||||
assertEquals(i, Validator.requireInRange(i, min, max));
|
||||
assertEquals(i, Validator.requireInRange(i, min, max, "Message"));
|
||||
assertEquals(i, Validator.requireInRange(i, min, max, () -> "Message"));
|
||||
}
|
||||
for( int ii = 0; ii < min; ii++ ) {
|
||||
final int i = ii;
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(i, min, max));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(i, min, max));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(i, min, max));
|
||||
}
|
||||
for( int ii = max+1; ii < 2*max; ii++ ) {
|
||||
final int i = ii;
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(i, min, max));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(i, min, max));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(i, min, max));
|
||||
}
|
||||
|
||||
double dmin = 50, dmax = 60;
|
||||
for( double dd = dmin; dd < dmax; dd+=0.1 ) {
|
||||
final double d = dd;
|
||||
assertEquals(d, Validator.requireInRange(d, dmin, dmax));
|
||||
assertEquals(d, Validator.requireInRange(d, dmin, dmax, "Message"));
|
||||
assertEquals(d, Validator.requireInRange(d, dmin, dmax, () -> "Message"));
|
||||
}
|
||||
for( double dd = dmin-10.0; dd < dmin; dd+=0.1 ) {
|
||||
final double d = dd;
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(d, dmin, dmax));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(d, dmin, dmax));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(d, dmin, dmax));
|
||||
}
|
||||
for( double dd = dmax+0.1; dd < dmax+10.0; dd+=0.1 ) {
|
||||
final double d = dd;
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(d, dmin, dmax));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(d, dmin, dmax));
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> Validator.requireInRange(d, dmin, dmax));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/test/resources/WitchCraftIcons_122_t.PNG
Normal file
BIN
src/test/resources/WitchCraftIcons_122_t.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Reference in New Issue
Block a user