mirror of
https://github.com/jneug/zeichenmaschine.git
synced 2026-04-14 14:43:33 +02:00
Merge branch 'main' into games
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,6 +34,7 @@ hs_err_pid*
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
|
local.properties
|
||||||
**/build/
|
**/build/
|
||||||
!src/**/build/
|
!src/**/build/
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ und diese Projekt folgt [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||||||
- Dokumentation erweitert.
|
- Dokumentation erweitert.
|
||||||
- Caching-Mechanismen in Klasse `util.Cache` ausgelagert.
|
- Caching-Mechanismen in Klasse `util.Cache` ausgelagert.
|
||||||
- `util.io.ImageLoader` und `util.io.FontLoader` verwenden `Cache`.
|
- `util.io.ImageLoader` und `util.io.FontLoader` verwenden `Cache`.
|
||||||
|
- `mouseWheelMoved` Eventhandler für Mausrad.
|
||||||
|
- `DrawingLayer.imageRotate(...)` Methoden, um Bilder um ihr Zentrum gedreht zu zeichnen.
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
- Die Methoden in `Validator` erwarten nun als zweiten Parameter den Namen des Parameters, der geprüft wird.
|
- Die Methoden in `Validator` erwarten nun als zweiten Parameter den Namen des Parameters, der geprüft wird.
|
||||||
|
- `DrawingLayer.image(...)` mit Größenänderung umbenannt zu `imageScale(...)`.
|
||||||
|
- Klassen in `schule.ngb.zm.util.io` werfen nun nur eine Warnung ohne Stack-Trace, wenn die Ressource nicht gefunden werden konnte.
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
- `Constants.choice(int...)` und `Constants.choice(double...)` wiederhergestellt.
|
- `Constants.choice(int...)` und `Constants.choice(double...)` wiederhergestellt.
|
||||||
|
|||||||
58
build.gradle
58
build.gradle
@@ -1,10 +1,11 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'java-library'
|
id 'java-library'
|
||||||
|
id 'org.hidetake.ssh' version '2.10.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'schule.ngb'
|
group 'schule.ngb'
|
||||||
version '0.0.34-SNAPSHOT'
|
version '0.0.35-SNAPSHOT'
|
||||||
|
|
||||||
java {
|
java {
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
@@ -19,6 +20,15 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remotes {
|
||||||
|
uberspace {
|
||||||
|
host = 'westphal.uberspace.de'
|
||||||
|
user = 'ngb'
|
||||||
|
identity = file("${System.properties['user.home']}/.ssh/uberspace_rsa")
|
||||||
|
knownHosts = allowAnyHosts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
runtimeOnly 'com.googlecode.soundlibs:jlayer:1.0.1.4'
|
runtimeOnly 'com.googlecode.soundlibs:jlayer:1.0.1.4'
|
||||||
runtimeOnly 'com.googlecode.soundlibs:tritonus-share:0.3.7.4'
|
runtimeOnly 'com.googlecode.soundlibs:tritonus-share:0.3.7.4'
|
||||||
@@ -92,6 +102,52 @@ task buildDocs {
|
|||||||
dependsOn 'mkdocs'
|
dependsOn 'mkdocs'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task zipSite(type: Zip) {
|
||||||
|
group "documentation"
|
||||||
|
description "Create zip archives for documentations"
|
||||||
|
|
||||||
|
dependsOn 'mkdocs'
|
||||||
|
|
||||||
|
from fileTree("${buildDir}/docs/site")
|
||||||
|
exclude '*.py'
|
||||||
|
exclude '__pycache__'
|
||||||
|
archiveName 'site.zip'
|
||||||
|
destinationDir(file("${buildDir}/docs"))
|
||||||
|
}
|
||||||
|
|
||||||
|
task zipJavadoc(type: Zip) {
|
||||||
|
group "documentation"
|
||||||
|
description "Create zip archives for javadoc"
|
||||||
|
|
||||||
|
dependsOn 'javadoc'
|
||||||
|
|
||||||
|
from fileTree("${buildDir}/docs/javadoc")
|
||||||
|
archiveName 'javadoc.zip'
|
||||||
|
destinationDir(file("${buildDir}/docs"))
|
||||||
|
}
|
||||||
|
|
||||||
|
task uploadDocs {
|
||||||
|
group "documentation"
|
||||||
|
description "Run all documentation tasks and upload artifacts to zeichenmaschine.xyz"
|
||||||
|
|
||||||
|
dependsOn 'zipSite'
|
||||||
|
dependsOn 'zipJavadoc'
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
ssh.run {
|
||||||
|
session(remotes.uberspace) {
|
||||||
|
execute 'rm -rf /var/www/virtual/ngb/zeichenmaschine.xyz/*', ignoreError: true
|
||||||
|
|
||||||
|
put from: "${buildDir}/docs/site.zip", into: '/var/www/virtual/ngb/zeichenmaschine.xyz', ignoreError: true
|
||||||
|
execute 'unzip -o -q /var/www/virtual/ngb/zeichenmaschine.xyz/site.zip -d /var/www/virtual/ngb/zeichenmaschine.xyz'
|
||||||
|
|
||||||
|
put from: "${buildDir}/docs/javadoc.zip", into: '/var/www/virtual/ngb/zeichenmaschine.xyz', ignoreError: true
|
||||||
|
execute 'unzip -o -q /var/www/virtual/ngb/zeichenmaschine.xyz/javadoc.zip -d /var/www/virtual/ngb/zeichenmaschine.xyz/docs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ leichter nutzbar machen.
|
|||||||
|
|
||||||
## Dokumentation
|
## Dokumentation
|
||||||
|
|
||||||
* [Schnellstart](quickstart.md)
|
* [Schnellstart](schnellstart.md)
|
||||||
* [Installation](installation.md)
|
* [Installation](installation.md)
|
||||||
* {{ javadoc_link() }}
|
* [Javadoc]({{ javadoc() }})
|
||||||
|
|
||||||
## Über die Zeichenmaschine
|
## Über die Zeichenmaschine
|
||||||
|
|
||||||
@@ -68,3 +68,4 @@ Alternativen, von deren Nutzung gar nicht abgeraten werden soll.
|
|||||||
Klassen, Methoden und Variablen verwendet. Ausnahme sind einzelne Klassen,
|
Klassen, Methoden und Variablen verwendet. Ausnahme sind einzelne Klassen,
|
||||||
die im Zusammnehang mit dem Namen der Bibliothek stehen, wie die
|
die im Zusammnehang mit dem Namen der Bibliothek stehen, wie die
|
||||||
Hauptklasse `Zeichenmaschine`.
|
Hauptklasse `Zeichenmaschine`.
|
||||||
|
|
||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
Um ein einfaches Projekt mit der **Zeichenmaschine** aufzusetzen ist nicht mehr
|
Um ein einfaches Projekt mit der **Zeichenmaschine** aufzusetzen ist nicht mehr
|
||||||
nötig, als
|
nötig, als
|
||||||
die [JAR-Datei der aktuellen Version](https://github.com/jneug/zeichenmaschine/release/latest)
|
die [JAR-Datei der aktuellen Version](https://github.com/jneug/zeichenmaschine/releases/latest)
|
||||||
herunterzuladen und dem *Classpath* des Projekts hinzuzufügen. Beschreibungen
|
herunterzuladen und dem *Classpath* des Projekts hinzuzufügen. Beschreibungen
|
||||||
für
|
für verschiedene Entwicklungsumgebungen sind hier aufgelistet.
|
||||||
verschiedene Entwicklungsumgebungen sind hier aufgelistet.
|
|
||||||
|
|
||||||
## Integration in Entwicklungsumgebungen
|
## Integration in Entwicklungsumgebungen
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ erstellt und in einem Fenster mit dem Titel „Shapes“ angezeigt.
|
|||||||
|
|
||||||
### Formen zeichnen
|
### Formen zeichnen
|
||||||
|
|
||||||
Eine Zeichenmaschine hat verschiedene Möglichkeiten, Inhalte in das
|
Eine Zeichenmaschine hat verschiedene Möglichkeiten Inhalte in das
|
||||||
Zeichenfenster zu zeichnen. Um ein einfaches statisches Bild zu erzeugen,
|
Zeichenfenster zu zeichnen. Um ein einfaches statisches Bild zu erzeugen,
|
||||||
überschreiben wir die {{ jdl("schule.ngb.zm.Zeichenmaschine", "draw()",
|
überschreiben wir die {{ jdl("schule.ngb.zm.Zeichenmaschine", "draw()",
|
||||||
c=False) }} Methode.
|
c=False) }} Methode.
|
||||||
@@ -146,7 +146,7 @@ Im Beispiel setzen wir nun die Grundeinstellungen in der `setup()` Methode. In
|
|||||||
## Interaktionen mit der Maus: Whack-a-mole
|
## Interaktionen mit der Maus: Whack-a-mole
|
||||||
|
|
||||||
Mit der Zeichenmaschine lassen sich Interaktionen mit der Maus leicht umsetzen.
|
Mit der Zeichenmaschine lassen sich Interaktionen mit der Maus leicht umsetzen.
|
||||||
Wor wollen das Beispielprogramm zu einem
|
Wir wollen das Beispielprogramm zu einem
|
||||||
[Whac-A-Mole](https://de.wikipedia.org/wiki/Whac-A-Mole) Spiel erweitern.
|
[Whac-A-Mole](https://de.wikipedia.org/wiki/Whac-A-Mole) Spiel erweitern.
|
||||||
|
|
||||||
Auf der Zeichenfläche wird nur noch ein gelber Kreis an einer zufälligen Stelle
|
Auf der Zeichenfläche wird nur noch ein gelber Kreis an einer zufälligen Stelle
|
||||||
@@ -219,7 +219,7 @@ aber auch abnehmen und stellt eine Methode dafür bereit
|
|||||||
Um auf einen Mausklick zu reagieren, ergänzen wir die
|
Um auf einen Mausklick zu reagieren, ergänzen wir die
|
||||||
{{ jdm("Zeichenmaschine", "mouseClicked()") }} Methode:
|
{{ jdm("Zeichenmaschine", "mouseClicked()") }} Methode:
|
||||||
|
|
||||||
```
|
```java
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked() {
|
public void mouseClicked() {
|
||||||
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
|
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
|
||||||
@@ -290,8 +290,8 @@ angeklickt wird.
|
|||||||
## Ein paar Details zur Zeichenmaschine
|
## Ein paar Details zur Zeichenmaschine
|
||||||
|
|
||||||
Die _Zeichenmaschine_ wurde stark von der kreativen Programmierumgebung
|
Die _Zeichenmaschine_ wurde stark von der kreativen Programmierumgebung
|
||||||
[Processing](https://processing.org) inspiriert. Wenn Du Processing schon
|
[Processing](https://processing.org) inspiriert. Wenn du Processing schon
|
||||||
kennst, dann werden Dir einige der Konzepte der _Zeichenmaschine_ schon bekannt
|
kennst, dann werden dir einige der Konzepte der _Zeichenmaschine_ schon bekannt
|
||||||
vorkommen.
|
vorkommen.
|
||||||
|
|
||||||
### Farben
|
### Farben
|
||||||
@@ -392,14 +392,14 @@ Sekunde aufgerufen wird. Normalerweise passiert dies genau 60-mal pro Sekunde.
|
|||||||
### Lebenszeit eines Kreises
|
### Lebenszeit eines Kreises
|
||||||
|
|
||||||
Jeder Kreis soll drei Sekunden zu sehen sein. Daher fügen wir eine neue
|
Jeder Kreis soll drei Sekunden zu sehen sein. Daher fügen wir eine neue
|
||||||
Objektvariable namens `moleTime` ein, die zunächst auf drei steht. Da wir auch
|
Objektvariable namens `moleTime` ein, die zunächst auf Drei steht. Da wir auch
|
||||||
Bruchteile von Skeunden abziehen wollen, wählen wir als Datentyp `double`:
|
Bruchteile von Sekunden abziehen wollen, wählen wir als Datentyp `double`:
|
||||||
|
|
||||||
```Java
|
```Java
|
||||||
private double moleTime=3.0;
|
private double moleTime=3.0;
|
||||||
```
|
```
|
||||||
|
|
||||||
Der Parameter `delta`, der `update()` Methode ist der Zeitraum in Sekunden, seit
|
Der Parameter `delta` der `update()` Methode ist der Zeitraum in Sekunden seit
|
||||||
dem letzten Frame. Subtrahieren wir diesen Wert bei jedem Frame von `moleTime`,
|
dem letzten Frame. Subtrahieren wir diesen Wert bei jedem Frame von `moleTime`,
|
||||||
wird der Wert immer kleiner. Dann müssen wir nur noch prüfen, ob er kleiner Null
|
wird der Wert immer kleiner. Dann müssen wir nur noch prüfen, ob er kleiner Null
|
||||||
ist und in dem Fall den Kreis auf eine neue Position springen lassen.
|
ist und in dem Fall den Kreis auf eine neue Position springen lassen.
|
||||||
@@ -489,7 +489,7 @@ drawing.circle(moleX,moleY,moleRadius*(moleTime/3.0));
|
|||||||
|
|
||||||
### Punktezähler
|
### Punktezähler
|
||||||
|
|
||||||
Zum Schluss wollen wir noch bei jedem Treffer mit der Maus die Punkte Zählen und
|
Zum Schluss wollen wir noch bei jedem Treffer mit der Maus die Punkte zählen und
|
||||||
als Text auf die Zeichenfläche schreiben.
|
als Text auf die Zeichenfläche schreiben.
|
||||||
|
|
||||||
Dazu ergänzen wir eine weitere Objektvariable `score`, die in `mouseClicked()`
|
Dazu ergänzen wir eine weitere Objektvariable `score`, die in `mouseClicked()`
|
||||||
@@ -589,18 +589,18 @@ drawing.setFillColor(BLACK);
|
|||||||
|
|
||||||
## Wie es weitergehen kann
|
## Wie es weitergehen kann
|
||||||
|
|
||||||
In diesem Schnellstart-Tutorial hast Du die Grundlagen der _Zeichenmaschine_
|
In diesem Schnellstart-Tutorial hast du die Grundlagen der _Zeichenmaschine_
|
||||||
gelernt. Um weiterzumachen, kannst Du versuchen, das Whack-a-mole Spiel um diese
|
gelernt. Um weiterzumachen, kannst du versuchen, das Whack-a-mole Spiel um diese
|
||||||
Funktionen zu erweitern:
|
Funktionen zu erweitern:
|
||||||
|
|
||||||
- Mehrere "Maulwürfe" gleichzeitig.
|
- Mehrere "Maulwürfe" gleichzeitig.
|
||||||
- Unterschiedliche Zeiten pro Maulwurf.
|
- Unterschiedliche Zeiten pro Maulwurf.
|
||||||
|
|
||||||
Wenn Du mehr über die Möglichkeiten lernen möchtest, die Dir die
|
Wenn du mehr über die Möglichkeiten lernen möchtest, die dir die
|
||||||
_Zeichenmaschine_ bereitstellt, kannst Du Dir die weiteren Tutorials in dieser
|
_Zeichenmaschine_ bereitstellt, kannst du dir die weiteren Tutorials in dieser
|
||||||
Dokumentation ansehen. Ein guter Startpunkt ist das
|
Dokumentation ansehen. Ein guter Startpunkt ist das
|
||||||
[Aquarium](tutorials/aquarium/aquarium1.md).
|
[Aquarium](tutorials/aquarium/aquarium1.md).
|
||||||
|
|
||||||
Viele verschiedene Beispiele, ohne detailliertes Tutorial, findest Du in der
|
Viele verschiedene Beispiele, ohne detailliertes Tutorial, findest du in der
|
||||||
Kategorie Beispiele und auf GitHub im Repository
|
Kategorie Beispiele und auf GitHub im Repository
|
||||||
[jneug/zeichenmaschine-examples](https://github.com/jneug/zeichenmaschine-examples).
|
[jneug/zeichenmaschine-examples](https://github.com/jneug/zeichenmaschine-examples).
|
||||||
@@ -8,7 +8,7 @@ site_dir: build/docs/site
|
|||||||
|
|
||||||
theme:
|
theme:
|
||||||
name: material
|
name: material
|
||||||
custom_dir: docs/home_override/
|
# custom_dir: docs/home_override/
|
||||||
language: de
|
language: de
|
||||||
logo: assets/icon_64.png
|
logo: assets/icon_64.png
|
||||||
favicon: assets/icon_32.png
|
favicon: assets/icon_32.png
|
||||||
@@ -37,7 +37,7 @@ extra_css:
|
|||||||
- assets/zmstyles.css
|
- assets/zmstyles.css
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- Einführung: einfuehrung.md
|
- Einführung: index.md
|
||||||
- Schnellstart: schnellstart.md
|
- Schnellstart: schnellstart.md
|
||||||
- Installation: installation.md
|
- Installation: installation.md
|
||||||
- Tutorials:
|
- Tutorials:
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
*/
|
*/
|
||||||
protected Options.StrokeType strokeType = SOLID;
|
protected Options.StrokeType strokeType = SOLID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Art der Kantenverbindungen von Linien.
|
||||||
|
*/
|
||||||
|
protected Options.StrokeJoin strokeJoin = MITER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache für den aktuellen {@code Stroke} der Kontur. Wird nach Änderung
|
* Cache für den aktuellen {@code Stroke} der Kontur. Wird nach Änderung
|
||||||
* einer der Kontureigenschaften auf {@code null} gesetzt und beim nächsten
|
* einer der Kontureigenschaften auf {@code null} gesetzt und beim nächsten
|
||||||
@@ -53,6 +58,7 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
*/
|
*/
|
||||||
protected MultipleGradientPaint fill = null;
|
protected MultipleGradientPaint fill = null;
|
||||||
|
|
||||||
|
// TODO: Add TexturePaint fill (https://docs.oracle.com/javase/8/docs//api/java/awt/TexturePaint.html)
|
||||||
|
|
||||||
// Implementierung Drawable Interface
|
// Implementierung Drawable Interface
|
||||||
|
|
||||||
@@ -154,7 +160,7 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
@Override
|
@Override
|
||||||
public Stroke getStroke() {
|
public Stroke getStroke() {
|
||||||
if( stroke == null ) {
|
if( stroke == null ) {
|
||||||
stroke = Strokeable.createStroke(strokeType, strokeWeight);
|
stroke = Strokeable.createStroke(strokeType, strokeWeight, strokeJoin);
|
||||||
}
|
}
|
||||||
return stroke;
|
return stroke;
|
||||||
}
|
}
|
||||||
@@ -191,4 +197,15 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
this.stroke = null;
|
this.stroke = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options.StrokeJoin getStrokeJoin() {
|
||||||
|
return strokeJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStrokeJoin( Options.StrokeJoin join ) {
|
||||||
|
strokeJoin = join;
|
||||||
|
this.stroke = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -576,6 +576,18 @@ public class Color implements Paint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double compare( Color color ) {
|
||||||
|
double maxDist = 764.8333151739665;
|
||||||
|
|
||||||
|
// see: https://www.compuphase.com/cmetric.htm
|
||||||
|
long rmean = (getRed() + color.getRed()) / 2;
|
||||||
|
long r = getRed() - color.getRed();
|
||||||
|
long g = getGreen() - color.getGreen();
|
||||||
|
long b = getBlue() - color.getBlue();
|
||||||
|
|
||||||
|
return 1.0 - (Math.sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8)) / maxDist);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft, ob ein anderes Objekt zu diesem gleich ist.
|
* Prüft, ob ein anderes Objekt zu diesem gleich ist.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.function.DoubleUnaryOperator;
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ public class Constants {
|
|||||||
/**
|
/**
|
||||||
* Patchversion der Zeichenmaschine.
|
* Patchversion der Zeichenmaschine.
|
||||||
*/
|
*/
|
||||||
public static final int APP_VERSION_REV = 34;
|
public static final int APP_VERSION_REV = 35;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Version der Zeichenmaschine als Text-String.
|
* Version der Zeichenmaschine als Text-String.
|
||||||
@@ -170,6 +171,21 @@ public class Constants {
|
|||||||
*/
|
*/
|
||||||
public static final Options.StrokeType DOTTED = Options.StrokeType.DOTTED;
|
public static final Options.StrokeType DOTTED = Options.StrokeType.DOTTED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option für abgerundete Kantenverbindungen von Konturen und Linien.
|
||||||
|
*/
|
||||||
|
public static final Options.StrokeJoin ROUND = Options.StrokeJoin.ROUND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option für abgeschnittene Kantenverbindungen von Konturen und Linien.
|
||||||
|
*/
|
||||||
|
public static final Options.StrokeJoin BEVEL = Options.StrokeJoin.BEVEL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option für eckige Kantenverbindungen von Konturen und Linien.
|
||||||
|
*/
|
||||||
|
public static final Options.StrokeJoin MITER = Options.StrokeJoin.MITER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Option für Pfeile mit Strichen als Kopf.
|
* Option für Pfeile mit Strichen als Kopf.
|
||||||
*/
|
*/
|
||||||
@@ -1268,7 +1284,7 @@ public class Constants {
|
|||||||
*
|
*
|
||||||
* @return Die {@code Random}-Instanz.
|
* @return Die {@code Random}-Instanz.
|
||||||
*/
|
*/
|
||||||
private static Random getRandom() {
|
public static Random getRandom() {
|
||||||
if( random == null ) {
|
if( random == null ) {
|
||||||
random = new Random();
|
random = new Random();
|
||||||
}
|
}
|
||||||
@@ -1389,26 +1405,6 @@ public class Constants {
|
|||||||
return getRandom().nextGaussian();
|
return getRandom().nextGaussian();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wählt ein zufälliges Element aus dem Array aus.
|
|
||||||
*
|
|
||||||
* @param values Ein Array mit Werten, die zur Auswahl stehen.
|
|
||||||
* @return Ein zufälliges Element aus dem Array.
|
|
||||||
*/
|
|
||||||
public static final int choice( int... values ) {
|
|
||||||
return values[random(0, values.length - 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wählt ein zufälliges Element aus dem Array aus.
|
|
||||||
*
|
|
||||||
* @param values Ein Array mit Werten, die zur Auswahl stehen.
|
|
||||||
* @return Ein zufälliges Element aus dem Array.
|
|
||||||
*/
|
|
||||||
public static final double choice( double... values ) {
|
|
||||||
return values[random(0, values.length - 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wählt ein zufälliges Element aus dem Array aus.
|
* Wählt ein zufälliges Element aus dem Array aus.
|
||||||
*
|
*
|
||||||
@@ -1571,6 +1567,18 @@ public class Constants {
|
|||||||
return valueList.toArray(values);
|
return valueList.toArray(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bringt die Werte im Array in eine zufällige Reihenfolge.
|
||||||
|
*
|
||||||
|
* @param values Ein Array mit Werte, die gemischt werden sollen.
|
||||||
|
* @param <T> Datentyp der Elemente.
|
||||||
|
* @return Das Array in zufälliger Reihenfolge.
|
||||||
|
*/
|
||||||
|
public static final <T> List<T> shuffle( List<T> values ) {
|
||||||
|
Collections.shuffle(values, random);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Geteilte {@code Noise}-Instanz zur Erzeugung von Perlin-Noise.
|
* Geteilte {@code Noise}-Instanz zur Erzeugung von Perlin-Noise.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package schule.ngb.zm;
|
package schule.ngb.zm;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
import java.awt.geom.Arc2D;
|
import java.awt.geom.Arc2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,6 +32,36 @@ public final class Options {
|
|||||||
DOTTED
|
DOTTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linienstile für Konturlinien.
|
||||||
|
*/
|
||||||
|
public enum StrokeJoin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abgerundete Verbindungen.
|
||||||
|
*/
|
||||||
|
ROUND(BasicStroke.JOIN_ROUND),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abgeschnittene Verbindungen.
|
||||||
|
*/
|
||||||
|
BEVEL(BasicStroke.JOIN_BEVEL),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eckige Verbindungen.
|
||||||
|
*/
|
||||||
|
MITER(BasicStroke.JOIN_MITER);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Der entsprechende Wert der Konstanten in {@link java.awt}
|
||||||
|
*/
|
||||||
|
public final int awt_type;
|
||||||
|
|
||||||
|
StrokeJoin( int type ) {
|
||||||
|
awt_type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stile für Pfeilspitzen.
|
* Stile für Pfeilspitzen.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ public interface Strokeable extends Drawable {
|
|||||||
* @param weight Die Dicke der Konturlinie.
|
* @param weight Die Dicke der Konturlinie.
|
||||||
*/
|
*/
|
||||||
default void setStrokeWeight( double weight ) {
|
default void setStrokeWeight( double weight ) {
|
||||||
setStroke(createStroke(getStrokeType(), weight));
|
setStroke(createStroke(getStrokeType(), weight, getStrokeJoin()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,7 +193,26 @@ public interface Strokeable extends Drawable {
|
|||||||
* @see Options.StrokeType
|
* @see Options.StrokeType
|
||||||
*/
|
*/
|
||||||
default void setStrokeType( Options.StrokeType type ) {
|
default void setStrokeType( Options.StrokeType type ) {
|
||||||
setStroke(createStroke(type, getStrokeWeight()));
|
setStroke(createStroke(type, getStrokeWeight(), getStrokeJoin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die Art der Konturverbindungen zurück.
|
||||||
|
*
|
||||||
|
* @return Die aktuelle Art der Konturverbindungen.
|
||||||
|
* @see Options.StrokeJoin
|
||||||
|
*/
|
||||||
|
Options.StrokeJoin getStrokeJoin();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Typ der Konturverbindungen. Erlaubte Werte sind {@link Constants#ROUND},
|
||||||
|
* {@link Constants#MITER} und {@link Constants#BEVEL}.
|
||||||
|
*
|
||||||
|
* @param join Eine der möglichen Konturverbindungen.
|
||||||
|
* @see Options.StrokeJoin
|
||||||
|
*/
|
||||||
|
default void setStrokeJoin( Options.StrokeJoin join ) {
|
||||||
|
setStroke(createStroke(getStrokeType(), getStrokeWeight(), join));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,26 +224,26 @@ public interface Strokeable extends Drawable {
|
|||||||
* @param strokeWeight
|
* @param strokeWeight
|
||||||
* @return Ein {@code Stroke} mit den passenden Kontureigenschaften.
|
* @return Ein {@code Stroke} mit den passenden Kontureigenschaften.
|
||||||
*/
|
*/
|
||||||
static Stroke createStroke( Options.StrokeType strokeType, double strokeWeight ) {
|
static Stroke createStroke( Options.StrokeType strokeType, double strokeWeight, Options.StrokeJoin strokeJoin ) {
|
||||||
switch( strokeType ) {
|
switch( strokeType ) {
|
||||||
case DOTTED:
|
case DOTTED:
|
||||||
return new BasicStroke(
|
return new BasicStroke(
|
||||||
(float) strokeWeight,
|
(float) strokeWeight,
|
||||||
BasicStroke.CAP_ROUND,
|
BasicStroke.CAP_ROUND,
|
||||||
BasicStroke.JOIN_ROUND,
|
strokeJoin.awt_type,
|
||||||
10.0f, new float[]{1.0f, 5.0f}, 0.0f);
|
10.0f, new float[]{1.0f, 5.0f}, 0.0f);
|
||||||
case DASHED:
|
case DASHED:
|
||||||
return new BasicStroke(
|
return new BasicStroke(
|
||||||
(float) strokeWeight,
|
(float) strokeWeight,
|
||||||
BasicStroke.CAP_ROUND,
|
BasicStroke.CAP_ROUND,
|
||||||
BasicStroke.JOIN_ROUND,
|
strokeJoin.awt_type,
|
||||||
10.0f, new float[]{5.0f}, 0.0f);
|
10.0f, new float[]{5.0f}, 0.0f);
|
||||||
case SOLID:
|
case SOLID:
|
||||||
default:
|
default:
|
||||||
return new BasicStroke(
|
return new BasicStroke(
|
||||||
(float) strokeWeight,
|
(float) strokeWeight,
|
||||||
BasicStroke.CAP_ROUND,
|
BasicStroke.CAP_ROUND,
|
||||||
BasicStroke.JOIN_ROUND);
|
strokeJoin.awt_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class Zeichenfenster extends JFrame {
|
|||||||
/**
|
/**
|
||||||
* Setzt das Look and Feel auf den Standard des Systems.
|
* Setzt das Look and Feel auf den Standard des Systems.
|
||||||
* <p>
|
* <p>
|
||||||
* Sollte einmalig vor erstellen des erstyen Programmfensters aufgerufen
|
* Sollte einmalig vor Erstellen des ersten Programmfensters aufgerufen
|
||||||
* werden.
|
* werden.
|
||||||
*/
|
*/
|
||||||
public static final void setLookAndFeel() {
|
public static final void setLookAndFeel() {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public class Zeichenmaschine extends Constants {
|
|||||||
*/
|
*/
|
||||||
private boolean running;
|
private boolean running;
|
||||||
|
|
||||||
private boolean terminateImediately = false;
|
private boolean terminateImmediately = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ob die ZM nach dem nächsten Frame pausiert werden soll.
|
* Ob die ZM nach dem nächsten Frame pausiert werden soll.
|
||||||
@@ -545,7 +545,7 @@ public class Zeichenmaschine extends Constants {
|
|||||||
|
|
||||||
if( running ) {
|
if( running ) {
|
||||||
running = false;
|
running = false;
|
||||||
terminateImediately = true;
|
terminateImmediately = true;
|
||||||
quitAfterShutdown = true;
|
quitAfterShutdown = true;
|
||||||
mainThread.interrupt();
|
mainThread.interrupt();
|
||||||
} else {
|
} else {
|
||||||
@@ -769,6 +769,23 @@ public class Zeichenmaschine extends Constants {
|
|||||||
framesPerSecond = framesPerSecondInternal;
|
framesPerSecond = framesPerSecondInternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt aus dem aktuellen Inhalt der {@link Zeichenleinwand} ein neues
|
||||||
|
* {@link BufferedImage}.
|
||||||
|
*/
|
||||||
|
public final BufferedImage getImage() {
|
||||||
|
BufferedImage img = ImageLoader.createImage(canvas.getWidth(), canvas.getHeight());
|
||||||
|
|
||||||
|
Graphics2D g = img.createGraphics();
|
||||||
|
// TODO: Transparente Hintergründe beim Speichern von png erlauben
|
||||||
|
g.setColor(DEFAULT_BACKGROUND.getJavaColor());
|
||||||
|
g.fillRect(0, 0, img.getWidth(), img.getHeight());
|
||||||
|
canvas.draw(g);
|
||||||
|
g.dispose();
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Speichert den aktuellen Inhalt der {@link Zeichenleinwand} in einer
|
* Speichert den aktuellen Inhalt der {@link Zeichenleinwand} in einer
|
||||||
* Bilddatei auf der Festplatte. Zur Auswahl der Zieldatei wird dem Nutzer
|
* Bilddatei auf der Festplatte. Zur Auswahl der Zieldatei wird dem Nutzer
|
||||||
@@ -794,24 +811,15 @@ public class Zeichenmaschine extends Constants {
|
|||||||
* Bilddatei im angegebenen Dateipfad auf der Festplatte.
|
* Bilddatei im angegebenen Dateipfad auf der Festplatte.
|
||||||
*/
|
*/
|
||||||
public final void saveImage( String filepath ) {
|
public final void saveImage( String filepath ) {
|
||||||
BufferedImage img = ImageLoader.createImage(canvas.getWidth(), canvas.getHeight());
|
|
||||||
|
|
||||||
Graphics2D g = img.createGraphics();
|
|
||||||
// TODO: Transparente Hintergründe beim Speichern von png erlauben
|
|
||||||
g.setColor(DEFAULT_BACKGROUND.getJavaColor());
|
|
||||||
g.fillRect(0, 0, img.getWidth(), img.getHeight());
|
|
||||||
canvas.draw(g);
|
|
||||||
g.dispose();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ImageLoader.saveImage(img, new File(filepath), true);
|
ImageLoader.saveImage(getImage(), new File(filepath), true);
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erstellt eine Momentanaufnahme des aktuellen Inhalts der
|
* Erstellt eine Momentaufnahme des aktuellen Inhalts der
|
||||||
* {@link Zeichenleinwand} und erstellt daraus eine
|
* {@link Zeichenleinwand} und erstellt daraus eine
|
||||||
* {@link ImageLayer Bildebene}. Die Ebene wird automatisch der
|
* {@link ImageLayer Bildebene}. Die Ebene wird automatisch der
|
||||||
* {@link Zeichenleinwand} vor dem {@link #background} hinzugefügt.
|
* {@link Zeichenleinwand} vor dem {@link #background} hinzugefügt.
|
||||||
@@ -1225,7 +1233,7 @@ public class Zeichenmaschine extends Constants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void mouseWheelMoved( MouseEvent e ) {
|
public void mouseWheelMoved( MouseEvent e ) {
|
||||||
mouseWheelMoved();
|
mouseMoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mouseWheelMoved() {
|
public void mouseWheelMoved() {
|
||||||
@@ -1399,7 +1407,7 @@ public class Zeichenmaschine extends Constants {
|
|||||||
|
|
||||||
if( Thread.interrupted() ) {
|
if( Thread.interrupted() ) {
|
||||||
running = false;
|
running = false;
|
||||||
terminateImediately = true;
|
terminateImmediately = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1455,7 +1463,7 @@ public class Zeichenmaschine extends Constants {
|
|||||||
}
|
}
|
||||||
state = Options.AppState.STOPPED;
|
state = Options.AppState.STOPPED;
|
||||||
// Shutdown the updateThread
|
// Shutdown the updateThread
|
||||||
while( !terminateImediately && updateThreadExecutor.isRunning() ) {
|
while( !terminateImmediately && updateThreadExecutor.isRunning() ) {
|
||||||
Thread.yield();
|
Thread.yield();
|
||||||
}
|
}
|
||||||
updateThreadExecutor.shutdownNow();
|
updateThreadExecutor.shutdownNow();
|
||||||
@@ -1562,7 +1570,7 @@ public class Zeichenmaschine extends Constants {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseWheelMoved( MouseWheelEvent e ) {
|
public void mouseWheelMoved( MouseWheelEvent e ) {
|
||||||
enqueueEvent(e);
|
// enqueueEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public abstract class Animation<T> extends Constants implements Updatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setEasing( DoubleUnaryOperator pEasing ) {
|
public void setEasing( DoubleUnaryOperator pEasing ) {
|
||||||
this.easing = pEasing;
|
this.easing = Validator.requireNotNull(pEasing, "easing");
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract T getAnimationTarget();
|
public abstract T getAnimationTarget();
|
||||||
@@ -61,7 +61,7 @@ public abstract class Animation<T> extends Constants implements Updatable {
|
|||||||
running = true;
|
running = true;
|
||||||
finished = false;
|
finished = false;
|
||||||
animate(easing.applyAsDouble(0.0));
|
animate(easing.applyAsDouble(0.0));
|
||||||
initializeEventDispatcher().dispatchEvent("start", this);
|
dispatchEvent("start");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void stop() {
|
public final void stop() {
|
||||||
@@ -70,7 +70,7 @@ public abstract class Animation<T> extends Constants implements Updatable {
|
|||||||
animate(easing.applyAsDouble((double) elapsedTime / (double) runtime));
|
animate(easing.applyAsDouble((double) elapsedTime / (double) runtime));
|
||||||
this.finish();
|
this.finish();
|
||||||
finished = true;
|
finished = true;
|
||||||
initializeEventDispatcher().dispatchEvent("stop", this);
|
dispatchEvent("stop");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
@@ -100,10 +100,9 @@ public abstract class Animation<T> extends Constants implements Updatable {
|
|||||||
|
|
||||||
double t = (double) elapsedTime / (double) runtime;
|
double t = (double) elapsedTime / (double) runtime;
|
||||||
if( t >= 1.0 ) {
|
if( t >= 1.0 ) {
|
||||||
running = false;
|
|
||||||
stop();
|
stop();
|
||||||
} else {
|
} else {
|
||||||
animate(easing.applyAsDouble(t));
|
animate(getEasing().applyAsDouble(t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +117,7 @@ public abstract class Animation<T> extends Constants implements Updatable {
|
|||||||
* e = Constants.limit(e, 0, 1);
|
* e = Constants.limit(e, 0, 1);
|
||||||
* </code></pre>
|
* </code></pre>
|
||||||
*
|
*
|
||||||
* @param e Fortschritt der Animation nachdem die Easingfunktion angewandt
|
* @param e Fortschritt der Animation, nachdem die Easing-Funktion angewandt
|
||||||
* wurde.
|
* wurde.
|
||||||
*/
|
*/
|
||||||
public abstract void animate( double e );
|
public abstract void animate( double e );
|
||||||
@@ -134,6 +133,12 @@ public abstract class Animation<T> extends Constants implements Updatable {
|
|||||||
return eventDispatcher;
|
return eventDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dispatchEvent( String type ) {
|
||||||
|
if( eventDispatcher != null ) {
|
||||||
|
eventDispatcher.dispatchEvent(type, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addListener( AnimationListener listener ) {
|
public void addListener( AnimationListener listener ) {
|
||||||
initializeEventDispatcher().addListener(listener);
|
initializeEventDispatcher().addListener(listener);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ import schule.ngb.zm.util.Validator;
|
|||||||
|
|
||||||
import java.util.function.DoubleUnaryOperator;
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eine Wrapper Animation, um die Werte einer anderen Animation (Laufzeit, Easing) zu überschrieben,
|
||||||
|
* ohne die Werte der Originalanimation zu verändern.
|
||||||
|
*
|
||||||
|
* @param <S> Art des Animierten Objektes.
|
||||||
|
*/
|
||||||
public class AnimationFacade<S> extends Animation<S> {
|
public class AnimationFacade<S> extends Animation<S> {
|
||||||
|
|
||||||
private Animation<S> anim;
|
private final Animation<S> anim;
|
||||||
|
|
||||||
public AnimationFacade( Animation<S> anim, int runtime, DoubleUnaryOperator easing ) {
|
public AnimationFacade( Animation<S> anim, int runtime, DoubleUnaryOperator easing ) {
|
||||||
super(runtime, easing);
|
super(runtime, easing);
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
package schule.ngb.zm.anim;
|
package schule.ngb.zm.anim;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.DoubleUnaryOperator;
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
// TODO: (ngb) Maybe use AnimationFacade to override runtime?
|
||||||
@SuppressWarnings( "unused" )
|
@SuppressWarnings( "unused" )
|
||||||
public class AnimationGroup<T> extends Animation<T> {
|
public class AnimationGroup<T> extends Animation<T> {
|
||||||
|
|
||||||
List<Animation<T>> anims;
|
private final List<Animation<T>> anims;
|
||||||
|
|
||||||
private boolean overrideEasing = false;
|
private final boolean overrideEasing;
|
||||||
|
|
||||||
private int overrideRuntime = -1;
|
private int overrideRuntime = -1;
|
||||||
|
|
||||||
private int lag = 0;
|
private final int lag;
|
||||||
|
|
||||||
private int active = 0;
|
private int active = 0;
|
||||||
|
|
||||||
|
public AnimationGroup( Animation<T>... anims ) {
|
||||||
|
this(0, -1, null, Arrays.asList(anims));
|
||||||
|
}
|
||||||
|
|
||||||
public AnimationGroup( Collection<Animation<T>> anims ) {
|
public AnimationGroup( Collection<Animation<T>> anims ) {
|
||||||
this(0, -1, null, anims);
|
this(0, -1, null, anims);
|
||||||
}
|
}
|
||||||
@@ -42,6 +48,8 @@ public class AnimationGroup<T> extends Animation<T> {
|
|||||||
if( easing != null ) {
|
if( easing != null ) {
|
||||||
this.easing = easing;
|
this.easing = easing;
|
||||||
overrideEasing = true;
|
overrideEasing = true;
|
||||||
|
} else {
|
||||||
|
overrideEasing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( runtime > 0 ) {
|
if( runtime > 0 ) {
|
||||||
@@ -64,52 +72,110 @@ public class AnimationGroup<T> extends Animation<T> {
|
|||||||
return anim.getAnimationTarget();
|
return anim.getAnimationTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if( this.finished ) {
|
||||||
return anims.get(anims.size() - 1).getAnimationTarget();
|
return anims.get(anims.size() - 1).getAnimationTarget();
|
||||||
|
} else {
|
||||||
|
return anims.get(0).getAnimationTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( double delta ) {
|
public DoubleUnaryOperator getEasing() {
|
||||||
elapsedTime += (int) (delta * 1000);
|
for( Animation<T> anim : anims ) {
|
||||||
// Animation is done. Stop all Animations.
|
if( anim.isActive() ) {
|
||||||
if( elapsedTime > runtime ) {
|
return anim.getEasing();
|
||||||
for( int i = 0; i < anims.size(); i++ ) {
|
|
||||||
if( anims.get(i).isActive() ) {
|
|
||||||
anims.get(i).elapsedTime = anims.get(i).runtime;
|
|
||||||
anims.get(i).stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
running = false;
|
if( this.finished ) {
|
||||||
this.stop();
|
return anims.get(anims.size() - 1).getEasing();
|
||||||
}
|
|
||||||
|
|
||||||
while( active < anims.size() && elapsedTime >= active * lag ) {
|
|
||||||
anims.get(active).start();
|
|
||||||
active += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for( int i = 0; i < active; i++ ) {
|
|
||||||
double t = 0.0;
|
|
||||||
if( overrideRuntime > 0 ) {
|
|
||||||
t = (double) (elapsedTime - i*lag) / (double) overrideRuntime;
|
|
||||||
} else {
|
} else {
|
||||||
t = (double) (elapsedTime - i*lag) / (double) anims.get(i).getRuntime();
|
return anims.get(0).getEasing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( t >= 1.0 ) {
|
// @Override
|
||||||
anims.get(i).elapsedTime = anims.get(i).runtime;
|
// public void update( double delta ) {
|
||||||
anims.get(i).stop();
|
// elapsedTime += (int) (delta * 1000);
|
||||||
} else {
|
//
|
||||||
double e = overrideEasing ?
|
// // Animation is done. Stop all Animations.
|
||||||
easing.applyAsDouble(t) :
|
// if( elapsedTime > runtime ) {
|
||||||
anims.get(i).easing.applyAsDouble(t);
|
// for( int i = 0; i < anims.size(); i++ ) {
|
||||||
|
// if( anims.get(i).isActive() ) {
|
||||||
|
// anims.get(i).elapsedTime = anims.get(i).runtime;
|
||||||
|
// anims.get(i).stop();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// elapsedTime = runtime;
|
||||||
|
// running = false;
|
||||||
|
// this.stop();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// while( active < anims.size() && elapsedTime >= active * lag ) {
|
||||||
|
// anims.get(active).start();
|
||||||
|
// active += 1;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for( int i = 0; i < active; i++ ) {
|
||||||
|
// double t = 0.0;
|
||||||
|
// if( overrideRuntime > 0 ) {
|
||||||
|
// t = (double) (elapsedTime - i*lag) / (double) overrideRuntime;
|
||||||
|
// } else {
|
||||||
|
// t = (double) (elapsedTime - i*lag) / (double) anims.get(i).getRuntime();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if( t >= 1.0 ) {
|
||||||
|
// anims.get(i).elapsedTime = anims.get(i).runtime;
|
||||||
|
// anims.get(i).stop();
|
||||||
|
// } else {
|
||||||
|
// double e = overrideEasing ?
|
||||||
|
// easing.applyAsDouble(t) :
|
||||||
|
// anims.get(i).easing.applyAsDouble(t);
|
||||||
|
//
|
||||||
|
// anims.get(i).animate(e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
anims.get(i).animate(e);
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
for( Animation<T> anim : anims ) {
|
||||||
|
if( anim.isActive() ) {
|
||||||
|
anim.elapsedTime = anim.runtime;
|
||||||
|
anim.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void animate( double e ) {
|
public void animate( double e ) {
|
||||||
|
while( active < anims.size() && elapsedTime >= active * lag ) {
|
||||||
|
anims.get(active).start();
|
||||||
|
active += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( int i = 0; i < active; i++ ) {
|
||||||
|
Animation<T> curAnim = anims.get(i);
|
||||||
|
|
||||||
|
double curRuntime = curAnim.getRuntime();
|
||||||
|
if( overrideRuntime > 0 ) {
|
||||||
|
curRuntime = overrideRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
double t = (double) (elapsedTime - i * lag) / (double) curRuntime;
|
||||||
|
if( t >= 1.0 ) {
|
||||||
|
curAnim.elapsedTime = curAnim.getRuntime();
|
||||||
|
curAnim.stop();
|
||||||
|
} else {
|
||||||
|
e = overrideEasing ?
|
||||||
|
easing.applyAsDouble(t) :
|
||||||
|
curAnim.easing.applyAsDouble(t);
|
||||||
|
|
||||||
|
curAnim.elapsedTime = (elapsedTime - i * lag);
|
||||||
|
curAnim.animate(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
144
src/main/java/schule/ngb/zm/anim/AnimationSequence.java
Normal file
144
src/main/java/schule/ngb/zm/anim/AnimationSequence.java
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package schule.ngb.zm.anim;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Führt eine Liste von Animationen nacheinander aus. Jede Animation startet direkt nachdem die
|
||||||
|
* davor geendet ist. Optional kann zwischen dem Ende einer und dem Start der nächsten Animation
|
||||||
|
* ein
|
||||||
|
* <var>lag</var> eingefügt werden.
|
||||||
|
*
|
||||||
|
* @param <T> Die Art des animierten Objektes.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "unused" )
|
||||||
|
public class AnimationSequence<T> extends Animation<T> {
|
||||||
|
|
||||||
|
private final List<Animation<T>> anims;
|
||||||
|
|
||||||
|
private final int lag;
|
||||||
|
|
||||||
|
private int currentAnimationIndex = -1, currentStart = -1, nextStart = -1;
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public AnimationSequence( Animation<T>... anims ) {
|
||||||
|
this(0, Arrays.asList(anims));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimationSequence( Collection<Animation<T>> anims ) {
|
||||||
|
this(0, anims);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimationSequence( int lag, Collection<Animation<T>> anims ) {
|
||||||
|
super(Easing::linear);
|
||||||
|
|
||||||
|
this.anims = List.copyOf(anims);
|
||||||
|
this.lag = lag;
|
||||||
|
|
||||||
|
this.runtime = (anims.size() - 1) * lag + anims.stream().mapToInt(Animation::getRuntime).sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getAnimationTarget() {
|
||||||
|
for( Animation<T> anim : anims ) {
|
||||||
|
if( anim.isActive() ) {
|
||||||
|
return anim.getAnimationTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( this.finished ) {
|
||||||
|
return anims.get(anims.size() - 1).getAnimationTarget();
|
||||||
|
} else {
|
||||||
|
return anims.get(0).getAnimationTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DoubleUnaryOperator getEasing() {
|
||||||
|
for( Animation<T> anim : anims ) {
|
||||||
|
if( anim.isActive() ) {
|
||||||
|
return anim.getEasing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( this.finished ) {
|
||||||
|
return anims.get(anims.size() - 1).getEasing();
|
||||||
|
} else {
|
||||||
|
return anims.get(0).getEasing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public void update( double delta ) {
|
||||||
|
// elapsedTime += (int) (delta * 1000);
|
||||||
|
//
|
||||||
|
// // Animation is done. Stop all Animations.
|
||||||
|
// if( elapsedTime > runtime ) {
|
||||||
|
// for( int i = 0; i < anims.size(); i++ ) {
|
||||||
|
// if( anims.get(i).isActive() ) {
|
||||||
|
// anims.get(i).elapsedTime = anims.get(i).runtime;
|
||||||
|
// anims.get(i).stop();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// elapsedTime = runtime;
|
||||||
|
// running = false;
|
||||||
|
// this.stop();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Animation<T> curAnim = null;
|
||||||
|
// if( elapsedTime > nextStart ) {
|
||||||
|
// currentAnimation += 1;
|
||||||
|
// curAnim = anims.get(currentAnimation);
|
||||||
|
// currentStart = nextStart;
|
||||||
|
// nextStart += lag + curAnim.getRuntime();
|
||||||
|
// curAnim.start();
|
||||||
|
// } else {
|
||||||
|
// curAnim = anims.get(currentAnimation);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Calculate delta for current animation
|
||||||
|
// double t = (double) (elapsedTime - currentStart) / (double) curAnim.getRuntime();
|
||||||
|
// if( t >= 1.0 ) {
|
||||||
|
// curAnim.elapsedTime = curAnim.runtime;
|
||||||
|
// curAnim.stop();
|
||||||
|
// } else {
|
||||||
|
// curAnim.animate(curAnim.easing.applyAsDouble(t));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
for( Animation<T> anim : anims ) {
|
||||||
|
if( anim.isActive() ) {
|
||||||
|
anim.elapsedTime = anim.runtime;
|
||||||
|
anim.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void animate( double e ) {
|
||||||
|
Animation<T> curAnim = null;
|
||||||
|
if( running && elapsedTime > nextStart ) {
|
||||||
|
currentAnimationIndex += 1;
|
||||||
|
curAnim = anims.get(currentAnimationIndex);
|
||||||
|
currentStart = nextStart;
|
||||||
|
nextStart += lag + curAnim.getRuntime();
|
||||||
|
curAnim.start();
|
||||||
|
} else {
|
||||||
|
curAnim = anims.get(currentAnimationIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate delta for current animation
|
||||||
|
double t = (double) (elapsedTime - currentStart) / (double) curAnim.getRuntime();
|
||||||
|
if( t >= 1.0 ) {
|
||||||
|
curAnim.elapsedTime = curAnim.runtime;
|
||||||
|
curAnim.stop();
|
||||||
|
} else {
|
||||||
|
curAnim.elapsedTime = (elapsedTime - currentStart);
|
||||||
|
curAnim.animate(curAnim.easing.applyAsDouble(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,33 +7,87 @@ import schule.ngb.zm.shapes.Shape;
|
|||||||
|
|
||||||
import java.util.function.DoubleUnaryOperator;
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates the {@code target} in a circular motion centered at (<var>cx</var>, <var>cy</var>).
|
||||||
|
*/
|
||||||
public class CircleAnimation extends Animation<Shape> {
|
public class CircleAnimation extends Animation<Shape> {
|
||||||
|
|
||||||
private Shape object;
|
private final Shape target;
|
||||||
|
|
||||||
private double centerx, centery, radius, startangle;
|
private final double centerX, centerY, rotateTo;
|
||||||
|
|
||||||
public CircleAnimation( Shape target, double cx, double cy, int runtime, DoubleUnaryOperator easing ) {
|
private double rotationRadius, startAngle;
|
||||||
|
|
||||||
|
private final boolean rotateRight;
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy ) {
|
||||||
|
this(target, cx, cy, 360, true, DEFAULT_ANIM_RUNTIME, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, double rotateTo ) {
|
||||||
|
this(target, cx, cy, rotateTo, true, DEFAULT_ANIM_RUNTIME, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, boolean rotateRight ) {
|
||||||
|
this(target, cx, cy, 360, rotateRight, DEFAULT_ANIM_RUNTIME, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, double rotateTo, boolean rotateRight ) {
|
||||||
|
this(target, cx, cy, rotateTo, rotateRight, DEFAULT_ANIM_RUNTIME, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, int runtime ) {
|
||||||
|
this(target, cx, cy, 360, true, runtime, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, boolean rotateRight, int runtime ) {
|
||||||
|
this(target, cx, cy, 360, rotateRight, runtime, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, DoubleUnaryOperator easing ) {
|
||||||
|
this(target, cx, cy, 360, true, DEFAULT_ANIM_RUNTIME, easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, boolean rotateRight, DoubleUnaryOperator easing ) {
|
||||||
|
this(target, cx, cy, 360, rotateRight, DEFAULT_ANIM_RUNTIME, easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, double rotateTo, int runtime, DoubleUnaryOperator easing ) {
|
||||||
|
this(target, cx, cy, rotateTo, true, runtime, easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CircleAnimation( Shape target, double cx, double cy, double rotateTo, boolean rotateRight, int runtime, DoubleUnaryOperator easing ) {
|
||||||
super(runtime, easing);
|
super(runtime, easing);
|
||||||
object = target;
|
this.target = target;
|
||||||
centerx = cx;
|
this.centerX = cx;
|
||||||
centery = cy;
|
this.centerY = cy;
|
||||||
Vector vec = new Vector(target.getX(), target.getY()).sub(cx, cy);
|
this.rotateTo = Constants.radians(Constants.limit(rotateTo, 0, 360));
|
||||||
startangle = vec.heading();
|
this.rotateRight = rotateRight;
|
||||||
radius = vec.length();
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
Vector vec = new Vector(target.getX(), target.getY()).sub(centerX, centerY);
|
||||||
|
startAngle = vec.heading();
|
||||||
|
rotationRadius = vec.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Shape getAnimationTarget() {
|
public Shape getAnimationTarget() {
|
||||||
return object;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void animate( double e ) {
|
public void animate( double e ) {
|
||||||
double angle = startangle + Constants.radians(Constants.interpolate(0, 360, e));
|
double angle = startAngle;
|
||||||
double x = centerx + radius * Constants.cos(angle);
|
if( rotateRight ) {
|
||||||
double y = centery + radius * Constants.sin(angle);
|
angle += Constants.interpolate(0, rotateTo, e);
|
||||||
object.moveTo(x, y);
|
} else {
|
||||||
|
angle -= Constants.interpolate(0, rotateTo, e);
|
||||||
|
}
|
||||||
|
double x = centerX + rotationRadius * Constants.cos(angle);
|
||||||
|
double y = centerY + rotationRadius * Constants.sin(angle);
|
||||||
|
target.moveTo(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ public class ContinousAnimation<T> extends Animation<T> {
|
|||||||
private int lag = 0;
|
private int lag = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Speichert eine Approximation der aktuellen Steigung der Easing-Funktion,
|
* Speichert eine Approximation der aktuellen Steigung der Easing-Funktion, um im Fall
|
||||||
* um im Fall {@code easeInOnly == true} nach dem ersten Durchlauf die
|
* {@code easeInOnly == true} nach dem ersten Durchlauf die passende Geschwindigkeit
|
||||||
* passende Geschwindigkeit beizubehalten.
|
* beizubehalten.
|
||||||
*/
|
*/
|
||||||
private double m = 1.0, lastEase = 0.0;
|
private double m = 1.0, lastEase = 0.0;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public class ContinousAnimation<T> extends Animation<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ContinousAnimation( Animation<T> baseAnimation, int lag, boolean easeInOnly ) {
|
private ContinousAnimation( Animation<T> baseAnimation, int lag, boolean easeInOnly ) {
|
||||||
super(baseAnimation.getRuntime(), baseAnimation.getEasing());
|
super(baseAnimation.getRuntime() + lag, baseAnimation.getEasing());
|
||||||
this.baseAnimation = baseAnimation;
|
this.baseAnimation = baseAnimation;
|
||||||
this.lag = lag;
|
this.lag = lag;
|
||||||
this.easeInOnly = easeInOnly;
|
this.easeInOnly = easeInOnly;
|
||||||
@@ -40,35 +40,80 @@ public class ContinousAnimation<T> extends Animation<T> {
|
|||||||
return baseAnimation.getAnimationTarget();
|
return baseAnimation.getAnimationTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRuntime() {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public void update( double delta ) {
|
||||||
|
// elapsedTime += (int) (delta * 1000);
|
||||||
|
// if( elapsedTime >= runtime + lag ) {
|
||||||
|
// elapsedTime %= (runtime + lag);
|
||||||
|
//
|
||||||
|
// if( easeInOnly && easing != null ) {
|
||||||
|
// easing = null;
|
||||||
|
// // runtime = (int)((1.0/m)*(runtime + lag));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// double t = (double) elapsedTime / (double) runtime;
|
||||||
|
// if( t >= 1.0 ) {
|
||||||
|
// t = 1.0;
|
||||||
|
// }
|
||||||
|
// if( easing != null ) {
|
||||||
|
// double e = easing.applyAsDouble(t);
|
||||||
|
// animate(e);
|
||||||
|
// m = (e-lastEase)/(delta*1000/(asDouble(runtime)));
|
||||||
|
// lastEase = e;
|
||||||
|
// } else {
|
||||||
|
// animate(t);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
baseAnimation.elapsedTime = baseAnimation.getRuntime();
|
||||||
|
baseAnimation.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
baseAnimation.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRuntime( int pRuntime ) {
|
||||||
|
baseAnimation.setRuntime(pRuntime);
|
||||||
|
runtime = pRuntime + lag;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( double delta ) {
|
public void update( double delta ) {
|
||||||
elapsedTime += (int) (delta * 1000);
|
int currentRuntime = elapsedTime + (int) (delta * 1000);
|
||||||
if( elapsedTime >= runtime + lag ) {
|
if( currentRuntime >= runtime + lag ) {
|
||||||
elapsedTime %= (runtime + lag);
|
elapsedTime = currentRuntime % (runtime + lag);
|
||||||
|
|
||||||
if( easeInOnly && easing != null ) {
|
if( easeInOnly && easing != null ) {
|
||||||
easing = null;
|
easing = Easing.linear();
|
||||||
// runtime = (int)((1.0/m)*(runtime + lag));
|
// runtime = (int)((1.0/m)*(runtime + lag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double t = (double) elapsedTime / (double) runtime;
|
super.update(delta);
|
||||||
if( t >= 1.0 ) {
|
|
||||||
t = 1.0;
|
|
||||||
}
|
|
||||||
if( easing != null ) {
|
|
||||||
double e = easing.applyAsDouble(t);
|
|
||||||
animate(e);
|
|
||||||
m = (e-lastEase)/(delta*1000/(asDouble(runtime)));
|
|
||||||
lastEase = e;
|
|
||||||
} else {
|
|
||||||
animate(t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void animate( double e ) {
|
public void animate( double e ) {
|
||||||
|
// double t = (double) elapsedTime / (double) runtime;
|
||||||
|
// if( t >= 1.0 ) {
|
||||||
|
// t = 1.0;
|
||||||
|
// }
|
||||||
|
baseAnimation.elapsedTime = elapsedTime;
|
||||||
baseAnimation.animate(e);
|
baseAnimation.animate(e);
|
||||||
|
m = (e - lastEase) / (delta * 1000 / (asDouble(runtime)));
|
||||||
|
lastEase = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,32 +13,51 @@ public class FadeAnimation extends Animation<Shape> {
|
|||||||
|
|
||||||
public static final int FADE_OUT = 0;
|
public static final int FADE_OUT = 0;
|
||||||
|
|
||||||
private Shape object;
|
private final Shape target;
|
||||||
|
|
||||||
|
private final int targetAlpha;
|
||||||
|
|
||||||
private Color fill, stroke;
|
private Color fill, stroke;
|
||||||
|
|
||||||
private int fillAlpha, strokeAlpha, tAlpha;
|
private int fillAlpha, strokeAlpha;
|
||||||
|
|
||||||
public FadeAnimation( Shape object, int alpha, int runtime, DoubleUnaryOperator easing ) {
|
public FadeAnimation( Shape target, int targetAlpha ) {
|
||||||
|
this(target, targetAlpha, DEFAULT_ANIM_RUNTIME, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FadeAnimation( Shape target, int targetAlpha, int runtime ) {
|
||||||
|
this(target, targetAlpha, runtime, DEFAULT_EASING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FadeAnimation( Shape target, int runtime, DoubleUnaryOperator easing ) {
|
||||||
|
this(target, 0, runtime, easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FadeAnimation( Shape target, int targetAlpha, int runtime, DoubleUnaryOperator easing ) {
|
||||||
super(runtime, easing);
|
super(runtime, easing);
|
||||||
|
|
||||||
this.object = object;
|
this.target = target;
|
||||||
fill = object.getFillColor();
|
this.targetAlpha = targetAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
fill = target.getFillColor();
|
||||||
fillAlpha = fill.getAlpha();
|
fillAlpha = fill.getAlpha();
|
||||||
stroke = object.getStrokeColor();
|
stroke = target.getStrokeColor();
|
||||||
strokeAlpha = stroke.getAlpha();
|
strokeAlpha = stroke.getAlpha();
|
||||||
tAlpha = alpha;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Shape getAnimationTarget() {
|
public Shape getAnimationTarget() {
|
||||||
return object;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void animate( double e ) {
|
public void animate( double e ) {
|
||||||
object.setFillColor(new Color(fill, (int) Constants.interpolate(fillAlpha, tAlpha, e)));
|
target.setFillColor(fill, (int) Constants.interpolate(fillAlpha, targetAlpha, e));
|
||||||
object.setStrokeColor(new Color(stroke, (int) Constants.interpolate(strokeAlpha, tAlpha, e)));
|
target.setStrokeColor(stroke, (int) Constants.interpolate(strokeAlpha, targetAlpha, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
package schule.ngb.zm.anim;
|
package schule.ngb.zm.anim;
|
||||||
|
|
||||||
import schule.ngb.zm.Color;
|
import schule.ngb.zm.Color;
|
||||||
import schule.ngb.zm.Constants;
|
|
||||||
import schule.ngb.zm.shapes.Shape;
|
import schule.ngb.zm.shapes.Shape;
|
||||||
|
|
||||||
import java.util.function.DoubleUnaryOperator;
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
public class FillAnimation extends Animation<Shape> {
|
public class FillAnimation extends Animation<Shape> {
|
||||||
|
|
||||||
private Shape object;
|
private final Shape object;
|
||||||
|
|
||||||
private Color oFill, tFill;
|
private Color originFill;
|
||||||
|
|
||||||
public FillAnimation( Shape object, Color newFill, int runtime, DoubleUnaryOperator easing ) {
|
private final Color targetFill;
|
||||||
|
|
||||||
|
public FillAnimation( Shape target, Color newFill, int runtime, DoubleUnaryOperator easing ) {
|
||||||
super(runtime, easing);
|
super(runtime, easing);
|
||||||
|
|
||||||
this.object = object;
|
this.object = target;
|
||||||
oFill = object.getFillColor();
|
targetFill = newFill;
|
||||||
tFill = newFill;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
originFill = object.getFillColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -27,7 +32,7 @@ public class FillAnimation extends Animation<Shape> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void animate( double e ) {
|
public void animate( double e ) {
|
||||||
object.setFillColor(Color.interpolate(oFill, tFill, e));
|
object.setFillColor(Color.interpolate(originFill, targetFill, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
package schule.ngb.zm.anim;
|
package schule.ngb.zm.anim;
|
||||||
|
|
||||||
import schule.ngb.zm.Color;
|
|
||||||
import schule.ngb.zm.Constants;
|
import schule.ngb.zm.Constants;
|
||||||
import schule.ngb.zm.shapes.Circle;
|
|
||||||
import schule.ngb.zm.shapes.Ellipse;
|
|
||||||
import schule.ngb.zm.shapes.Rectangle;
|
|
||||||
import schule.ngb.zm.shapes.Shape;
|
import schule.ngb.zm.shapes.Shape;
|
||||||
|
|
||||||
import java.util.function.DoubleUnaryOperator;
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
public class MoveAnimation extends Animation<Shape> {
|
public class MoveAnimation extends Animation<Shape> {
|
||||||
|
|
||||||
private Shape object;
|
private final Shape object;
|
||||||
|
|
||||||
private double oX, oY, tX, tY;
|
private final double targetX, targetY;
|
||||||
|
|
||||||
public MoveAnimation( Shape object, double x, double y, int runtime, DoubleUnaryOperator easing ) {
|
private double originX, originY;
|
||||||
|
|
||||||
|
|
||||||
|
public MoveAnimation( Shape target, double targetX, double targetY, int runtime, DoubleUnaryOperator easing ) {
|
||||||
super(runtime, easing);
|
super(runtime, easing);
|
||||||
|
|
||||||
this.object = object;
|
this.object = target;
|
||||||
oX = object.getX();
|
this.targetX = targetX;
|
||||||
oY = object.getY();
|
this.targetY = targetY;
|
||||||
tX = x;
|
}
|
||||||
tY = y;
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
originX = object.getX();
|
||||||
|
originY = object.getY();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -32,8 +35,8 @@ public class MoveAnimation extends Animation<Shape> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void animate( double e ) {
|
public void animate( double e ) {
|
||||||
object.setX(Constants.interpolate(oX, tX, e));
|
object.setX(Constants.interpolate(originX, targetX, e));
|
||||||
object.setY(Constants.interpolate(oY, tY, e));
|
object.setY(Constants.interpolate(originY, targetY, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,6 +404,11 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
return shapeDelegate.getStrokeType();
|
return shapeDelegate.getStrokeType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options.StrokeJoin getStrokeJoin() {
|
||||||
|
return shapeDelegate.getStrokeJoin();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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}.
|
||||||
@@ -980,7 +985,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @see ImageLoader#loadImage(String)
|
* @see ImageLoader#loadImage(String)
|
||||||
*/
|
*/
|
||||||
public void image( String imageSource, double x, double y ) {
|
public void image( String imageSource, double x, double y ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, 1.0, shapeDelegate.getAnchor());
|
imageScale(ImageLoader.loadImage(imageSource), x, y, 1.0, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -997,7 +1002,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @see ImageLoader#loadImage(String)
|
* @see ImageLoader#loadImage(String)
|
||||||
*/
|
*/
|
||||||
public void image( String imageSource, double x, double y, Options.Direction anchor ) {
|
public void image( String imageSource, double x, double y, Options.Direction anchor ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, 1.0, anchor);
|
imageScale(ImageLoader.loadImage(imageSource), x, y, 1.0, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1005,8 +1010,9 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* Koordinaten auf die Zeichenebene. Das Bild wird um den angegebenen Faktor
|
* Koordinaten auf die Zeichenebene. Das Bild wird um den angegebenen Faktor
|
||||||
* skaliert.
|
* skaliert.
|
||||||
* <p>
|
* <p>
|
||||||
* Siehe {@link #image(Image, double, double, double, Options.Direction)}
|
* Siehe
|
||||||
* für mehr Details.
|
* {@link #imageScale(Image, double, double, double, Options.Direction)} für
|
||||||
|
* mehr Details.
|
||||||
*
|
*
|
||||||
* @param imageSource Die Bildquelle.
|
* @param imageSource Die Bildquelle.
|
||||||
* @param x x-Koordinate des Ankerpunktes.
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
@@ -1014,8 +1020,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param scale Der Skalierungsfaktor des Bildes.
|
* @param scale Der Skalierungsfaktor des Bildes.
|
||||||
* @see ImageLoader#loadImage(String)
|
* @see ImageLoader#loadImage(String)
|
||||||
*/
|
*/
|
||||||
public void image( String imageSource, double x, double y, double scale ) {
|
public void imageScale( String imageSource, double x, double y, double scale ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, scale, shapeDelegate.getAnchor());
|
imageScale(ImageLoader.loadImage(imageSource), x, y, scale, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1023,8 +1029,9 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* Koordinaten auf die Zeichenebene. Das Bild wird um den angegebenen Faktor
|
* Koordinaten auf die Zeichenebene. Das Bild wird um den angegebenen Faktor
|
||||||
* skaliert und der angegebene Ankerpunkt verwendet.
|
* skaliert und der angegebene Ankerpunkt verwendet.
|
||||||
* <p>
|
* <p>
|
||||||
* Siehe {@link #image(Image, double, double, double, Options.Direction)}
|
* Siehe
|
||||||
* für mehr Details.
|
* {@link #imageScale(Image, double, double, double, Options.Direction)} für
|
||||||
|
* mehr Details.
|
||||||
*
|
*
|
||||||
* @param imageSource Die Bildquelle.
|
* @param imageSource Die Bildquelle.
|
||||||
* @param x x-Koordinate des Ankerpunktes.
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
@@ -1033,8 +1040,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param anchor Der Ankerpunkt.
|
* @param anchor Der Ankerpunkt.
|
||||||
* @see ImageLoader#loadImage(String)
|
* @see ImageLoader#loadImage(String)
|
||||||
*/
|
*/
|
||||||
public void image( String imageSource, double x, double y, double scale, Options.Direction anchor ) {
|
public void imageScale( String imageSource, double x, double y, double scale, Options.Direction anchor ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, scale, anchor);
|
imageScale(ImageLoader.loadImage(imageSource), x, y, scale, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1046,23 +1053,24 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param y y-Koordinate des Ankerpunktes.
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
*/
|
*/
|
||||||
public void image( Image image, double x, double y ) {
|
public void image( Image image, double x, double y ) {
|
||||||
image(image, x, y, 1.0, shapeDelegate.getAnchor());
|
imageScale(image, x, y, 1.0, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeichnet das angegebene Bild an den angegebenen Koordinaten auf die
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten auf die
|
||||||
* Zeichenebene. Das Bild wird um den angegebenen Faktor skaliert.
|
* Zeichenebene. Das Bild wird um den angegebenen Faktor skaliert.
|
||||||
* <p>
|
* <p>
|
||||||
* Siehe {@link #image(Image, double, double, double, Options.Direction)}
|
* Siehe
|
||||||
* für mehr Details.
|
* {@link #imageScale(Image, double, double, double, Options.Direction)} für
|
||||||
|
* mehr Details.
|
||||||
*
|
*
|
||||||
* @param image Das vorher geladene Bild.
|
* @param image Das vorher geladene Bild.
|
||||||
* @param x x-Koordinate des Ankerpunktes.
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
* @param y y-Koordinate des Ankerpunktes.
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
* @param scale Der Skalierungsfaktor des Bildes.
|
* @param scale Der Skalierungsfaktor des Bildes.
|
||||||
*/
|
*/
|
||||||
public void image( Image image, double x, double y, double scale ) {
|
public void imageScale( Image image, double x, double y, double scale ) {
|
||||||
image(image, x, y, scale, shapeDelegate.getAnchor());
|
imageScale(image, x, y, scale, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1077,8 +1085,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* Das Seitenverhältnis wird immer beibehalten.
|
* Das Seitenverhältnis wird immer beibehalten.
|
||||||
* <p>
|
* <p>
|
||||||
* Soll das Bild innerhalb eines vorgegebenen Rechtecks liegen, sollte
|
* Soll das Bild innerhalb eines vorgegebenen Rechtecks liegen, sollte
|
||||||
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
* {@link #imageScale(Image, double, double, double, double,
|
||||||
* verwendet werden.
|
* Options.Direction)} verwendet werden.
|
||||||
*
|
*
|
||||||
* @param image Das vorher geladene Bild.
|
* @param image Das vorher geladene Bild.
|
||||||
* @param x x-Koordinate des Ankerpunktes.
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
@@ -1086,7 +1094,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param scale Der Skalierungsfaktor des Bildes.
|
* @param scale Der Skalierungsfaktor des Bildes.
|
||||||
* @param anchor Der Ankerpunkt.
|
* @param anchor Der Ankerpunkt.
|
||||||
*/
|
*/
|
||||||
public void image( Image image, double x, double y, double scale, Options.Direction anchor ) {
|
public void imageScale( Image image, double x, double y, double scale, Options.Direction anchor ) {
|
||||||
/*if( image != null ) {
|
/*if( image != null ) {
|
||||||
double neww = image.getWidth(null) * scale;
|
double neww = image.getWidth(null) * scale;
|
||||||
double newh = image.getHeight(null) * scale;
|
double newh = image.getHeight(null) * scale;
|
||||||
@@ -1095,7 +1103,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}*/
|
}*/
|
||||||
double neww = image.getWidth(null) * scale;
|
double neww = image.getWidth(null) * scale;
|
||||||
double newh = image.getHeight(null) * scale;
|
double newh = image.getHeight(null) * scale;
|
||||||
image(image, x, y, neww, newh, anchor);
|
imageScale(image, x, y, neww, newh, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1103,8 +1111,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* Koordinaten in der angegebenen Größe auf die Zeichenebene.
|
* Koordinaten in der angegebenen Größe auf die Zeichenebene.
|
||||||
* <p>
|
* <p>
|
||||||
* Siehe
|
* Siehe
|
||||||
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
* {@link #imageScale(Image, double, double, double, double,
|
||||||
* für mehr Details.
|
* Options.Direction)} für mehr Details.
|
||||||
*
|
*
|
||||||
* @param imageSource Die Bildquelle.
|
* @param imageSource Die Bildquelle.
|
||||||
* @param x x-Koordinate des Ankerpunktes.
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
@@ -1113,8 +1121,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
* @see ImageLoader#loadImage(String)
|
* @see ImageLoader#loadImage(String)
|
||||||
*/
|
*/
|
||||||
public void image( String imageSource, double x, double y, double width, double height ) {
|
public void imageScale( String imageSource, double x, double y, double width, double height ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, width, height, shapeDelegate.getAnchor());
|
imageScale(ImageLoader.loadImage(imageSource), x, y, width, height, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1123,8 +1131,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* angegebene Ankerpunkt verwendet.
|
* angegebene Ankerpunkt verwendet.
|
||||||
* <p>
|
* <p>
|
||||||
* Siehe
|
* Siehe
|
||||||
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
* {@link #imageScale(Image, double, double, double, double,
|
||||||
* für mehr Details.
|
* Options.Direction)} für mehr Details.
|
||||||
*
|
*
|
||||||
* @param imageSource Die Bildquelle.
|
* @param imageSource Die Bildquelle.
|
||||||
* @param x x-Koordinate des Ankerpunktes.
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
@@ -1134,8 +1142,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param anchor Der Ankerpunkt.
|
* @param anchor Der Ankerpunkt.
|
||||||
* @see ImageLoader#loadImage(String)
|
* @see ImageLoader#loadImage(String)
|
||||||
*/
|
*/
|
||||||
public void image( String imageSource, double x, double y, double width, double height, Options.Direction anchor ) {
|
public void imageScale( String imageSource, double x, double y, double width, double height, Options.Direction anchor ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, width, height, anchor);
|
imageScale(ImageLoader.loadImage(imageSource), x, y, width, height, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1143,8 +1151,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* angegebenen Größe auf die Zeichenebene.
|
* angegebenen Größe auf die Zeichenebene.
|
||||||
* <p>
|
* <p>
|
||||||
* Siehe
|
* Siehe
|
||||||
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
* {@link #imageScale(Image, double, double, double, double,
|
||||||
* für mehr Details.
|
* Options.Direction)} für mehr Details.
|
||||||
*
|
*
|
||||||
* @param image Ein Bild-Objekt.
|
* @param image Ein Bild-Objekt.
|
||||||
* @param x x-Koordinate des Ankerpunktes.
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
@@ -1152,8 +1160,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
*/
|
*/
|
||||||
public void image( Image image, double x, double y, double width, double height ) {
|
public void imageScale( Image image, double x, double y, double width, double height ) {
|
||||||
image(image, x, y, width, height, shapeDelegate.getAnchor());
|
imageScale(image, x, y, width, height, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1171,7 +1179,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* <p>
|
* <p>
|
||||||
* Soll die Bildgröße unter Beachtung der Abmessungen um einen Faktor
|
* Soll die Bildgröße unter Beachtung der Abmessungen um einen Faktor
|
||||||
* verändert werden, sollte
|
* verändert werden, sollte
|
||||||
* {@link #image(Image, double, double, double, Options.Direction)}
|
* {@link #imageScale(Image, double, double, double, Options.Direction)}
|
||||||
* verwendet werden.
|
* verwendet werden.
|
||||||
*
|
*
|
||||||
* @param image Ein Bild-Objekt.
|
* @param image Ein Bild-Objekt.
|
||||||
@@ -1181,17 +1189,163 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
* @param anchor Der Ankerpunkt.
|
* @param anchor Der Ankerpunkt.
|
||||||
*/
|
*/
|
||||||
public void image( Image image, double x, double y, double width, double height, Options.Direction anchor ) {
|
public void imageScale( Image image, double x, double y, double width, double height, Options.Direction anchor ) {
|
||||||
|
imageRotateAndScale(image, x, y, 0, width, height, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Bildquelle an den angegebenen
|
||||||
|
* Koordinaten mit der angegebenen Drehung auf die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Das Bild wird um seinen Mittelpunkt als Rotationszentrum gedreht.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
*/
|
||||||
|
public void imageRotate( String imageSource, double x, double y, double angle ) {
|
||||||
|
imageRotate(ImageLoader.loadImage(imageSource), x, y, angle, shapeDelegate.getAnchor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Bildquelle an den angegebenen
|
||||||
|
* Koordinaten mit der angegebenen Drehung auf die Zeichenebene. Der
|
||||||
|
* angegebene Ankerpunkt wird verwendet.
|
||||||
|
* <p>
|
||||||
|
* Das Bild wird um seinen Mittelpunkt als Rotationszentrum gedreht.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
*/
|
||||||
|
public void imageRotate( String imageSource, double x, double y, double angle, Options.Direction anchor ) {
|
||||||
|
imageRotate(ImageLoader.loadImage(imageSource), x, y, angle, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten mit der
|
||||||
|
* angegebenen Drehung auf die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Das Bild wird um seinen Mittelpunkt als Rotationszentrum gedreht.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
*/
|
||||||
|
public void imageRotate( Image image, double x, double y, double angle ) {
|
||||||
|
imageRotateAndScale(image, x, y, angle, image.getWidth(null), image.getHeight(null), shapeDelegate.getAnchor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten mit der
|
||||||
|
* angegebenen Drehung auf die Zeichenebene. Der angegebene Ankerpunkt wird
|
||||||
|
* verwendet.
|
||||||
|
* <p>
|
||||||
|
* Das Bild wird um seinen Mittelpunkt als Rotationszentrum gedreht.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
*/
|
||||||
|
public void imageRotate( Image image, double x, double y, double angle, Options.Direction anchor ) {
|
||||||
|
imageRotateAndScale(image, x, y, angle, image.getWidth(null), image.getHeight(null), anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Bildquelle an den angegebenen
|
||||||
|
* Koordinaten mit der angegebenen Drehung in der angegebenen Größe auf die
|
||||||
|
* Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @see #imageRotate(String, double, double, double)
|
||||||
|
* @see #imageScale(Image, double, double, double)
|
||||||
|
*/
|
||||||
|
public void imageRotateAndScale( String imageSource, double x, double y, double angle, double width, double height ) {
|
||||||
|
imageRotateAndScale(ImageLoader.loadImage(imageSource), x, y, angle, width, height, shapeDelegate.getAnchor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Bildquelle an den angegebenen
|
||||||
|
* Koordinaten mit der angegebenen Drehung in der angegebenen Größe auf die
|
||||||
|
* Zeichenebene. Der angegebene Ankerpunkt wird verwendet.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
* @see #imageRotate(String, double, double, double)
|
||||||
|
* @see #imageScale(Image, double, double, double)
|
||||||
|
*/
|
||||||
|
public void imageRotateAndScale( String imageSource, double x, double y, double angle, double width, double height, Options.Direction anchor ) {
|
||||||
|
imageRotateAndScale(ImageLoader.loadImage(imageSource), x, y, angle, width, height, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten mit der
|
||||||
|
* angegebenen Drehung in der angegebenen Größe auf die Zeichenebene. Der
|
||||||
|
* angegebene Ankerpunkt wird verwendet.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @see #imageRotate(String, double, double, double)
|
||||||
|
* @see #imageScale(Image, double, double, double)
|
||||||
|
*/
|
||||||
|
public void imageRotateAndScale( Image image, double x, double y, double angle, double width, double height ) {
|
||||||
|
imageRotateAndScale(image, x, y, angle, width, height, shapeDelegate.getAnchor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten mit der
|
||||||
|
* angegebenen Drehung in der angegebenen Größe auf die Zeichenebene. Der
|
||||||
|
* angegebene Ankerpunkt wird verwendet.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param angle Winkel in Grad.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
* @see #imageRotate(String, double, double, double)
|
||||||
|
* @see #imageScale(Image, double, double, double)
|
||||||
|
*/
|
||||||
|
public void imageRotateAndScale( Image image, double x, double y, double angle, double width, double height, Options.Direction anchor ) {
|
||||||
// TODO: Use Validator or at least LOG a message if image == null?
|
// TODO: Use Validator or at least LOG a message if image == null?
|
||||||
if( image != null ) {
|
if( image != null ) {
|
||||||
|
AffineTransform orig = drawing.getTransform();
|
||||||
|
|
||||||
|
int imgWidth = image.getWidth(null);
|
||||||
|
int imgHeight = image.getHeight(null);
|
||||||
|
|
||||||
if( width == 0 ) {
|
if( width == 0 ) {
|
||||||
width = (height / image.getHeight(null)) * image.getWidth(null);
|
width = (height / imgHeight) * imgWidth;
|
||||||
} else if( height == 0 ) {
|
} else if( height == 0 ) {
|
||||||
height = (width / image.getWidth(null)) * image.getHeight(null);
|
height = (width / imgWidth) * imgHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point2D.Double anchorPoint = getOriginPoint(x, y, width, height, anchor);
|
Point2D.Double anchorPoint = getOriginPoint(x, y, width, height, anchor);
|
||||||
|
drawing.rotate(Math.toRadians(angle), anchorPoint.x + width / 2, anchorPoint.y + height / 2);
|
||||||
drawing.drawImage(image, (int) anchorPoint.x, (int) anchorPoint.y, (int) width, (int) height, null);
|
drawing.drawImage(image, (int) anchorPoint.x, (int) anchorPoint.y, (int) width, (int) height, null);
|
||||||
|
|
||||||
|
drawing.setTransform(orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package schule.ngb.zm.layers;
|
package schule.ngb.zm.layers;
|
||||||
|
|
||||||
import schule.ngb.zm.Layer;
|
import schule.ngb.zm.Layer;
|
||||||
|
import schule.ngb.zm.Updatable;
|
||||||
import schule.ngb.zm.anim.Animation;
|
import schule.ngb.zm.anim.Animation;
|
||||||
import schule.ngb.zm.anim.AnimationFacade;
|
import schule.ngb.zm.anim.AnimationFacade;
|
||||||
import schule.ngb.zm.anim.Easing;
|
import schule.ngb.zm.anim.Easing;
|
||||||
@@ -24,20 +25,26 @@ public class ShapesLayer extends Layer {
|
|||||||
*/
|
*/
|
||||||
protected boolean clearBeforeDraw = true;
|
protected boolean clearBeforeDraw = true;
|
||||||
|
|
||||||
private final List<Shape> shapes;
|
protected boolean updateShapes = true;
|
||||||
|
|
||||||
|
protected final List<Shape> shapes;
|
||||||
|
|
||||||
private final List<Animation<? extends Shape>> animations;
|
private final List<Animation<? extends Shape>> animations;
|
||||||
|
|
||||||
|
private final List<Updatable> updatables;
|
||||||
|
|
||||||
public ShapesLayer() {
|
public ShapesLayer() {
|
||||||
super();
|
super();
|
||||||
shapes = new LinkedList<>();
|
shapes = new LinkedList<>();
|
||||||
animations = new LinkedList<>();
|
animations = new LinkedList<>();
|
||||||
|
updatables = new LinkedList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShapesLayer( int width, int height ) {
|
public ShapesLayer( int width, int height ) {
|
||||||
super(width, height);
|
super(width, height);
|
||||||
shapes = new LinkedList<>();
|
shapes = new LinkedList<>();
|
||||||
animations = new LinkedList<>();
|
animations = new LinkedList<>();
|
||||||
|
updatables = new LinkedList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Shape getShape( int index ) {
|
public Shape getShape( int index ) {
|
||||||
@@ -70,12 +77,24 @@ public class ShapesLayer extends Layer {
|
|||||||
public void add( Shape... shapes ) {
|
public void add( Shape... shapes ) {
|
||||||
synchronized( this.shapes ) {
|
synchronized( this.shapes ) {
|
||||||
Collections.addAll(this.shapes, shapes);
|
Collections.addAll(this.shapes, shapes);
|
||||||
|
|
||||||
|
for( Shape s : shapes ) {
|
||||||
|
if( Updatable.class.isInstance(s) ) {
|
||||||
|
updatables.add((Updatable) s);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add( Collection<Shape> shapes ) {
|
public void add( Collection<Shape> shapes ) {
|
||||||
synchronized( this.shapes ) {
|
synchronized( this.shapes ) {
|
||||||
this.shapes.addAll(shapes);
|
this.shapes.addAll(shapes);
|
||||||
|
|
||||||
|
for( Shape s : shapes ) {
|
||||||
|
if( Updatable.class.isInstance(s) ) {
|
||||||
|
updatables.add((Updatable) s);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +158,27 @@ public class ShapesLayer extends Layer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( double delta ) {
|
public void update( double delta ) {
|
||||||
|
if( updateShapes ) {
|
||||||
|
synchronized( shapes ) {
|
||||||
|
List<Updatable> uit = List.copyOf(updatables);
|
||||||
|
for( Updatable u : uit ) {
|
||||||
|
if( u.isActive() ) {
|
||||||
|
u.update(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Iterator<Updatable> uit = updatables.iterator();
|
||||||
|
while( uit.hasNext() ) {
|
||||||
|
Updatable u = uit.next();
|
||||||
|
if( u.isActive() ) {
|
||||||
|
u.update(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
Iterator<Animation<? extends Shape>> it = animations.iterator();
|
Iterator<Animation<? extends Shape>> it = animations.iterator();
|
||||||
while( it.hasNext() ) {
|
while( it.hasNext() ) {
|
||||||
Animation<? extends Shape> anim = it.next();
|
Animation<? extends Shape> anim = it.next();
|
||||||
|
|||||||
@@ -230,6 +230,11 @@ public class TurtleLayer extends Layer implements Strokeable, Fillable {
|
|||||||
return mainTurtle.getStrokeType();
|
return mainTurtle.getStrokeType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options.StrokeJoin getStrokeJoin() {
|
||||||
|
return mainTurtle.getStrokeJoin();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setStrokeType( Options.StrokeType type ) {
|
public void setStrokeType( Options.StrokeType type ) {
|
||||||
mainTurtle.setStrokeType(type);
|
mainTurtle.setStrokeType(type);
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package schule.ngb.zm.shapes;
|
package schule.ngb.zm.shapes;
|
||||||
|
|
||||||
|
import schule.ngb.zm.Options;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.CubicCurve2D;
|
import java.awt.geom.CubicCurve2D;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.QuadCurve2D;
|
import java.awt.geom.QuadCurve2D;
|
||||||
@@ -170,6 +174,30 @@ public class Curve extends Shape {
|
|||||||
move(dx, dy);
|
move(dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw( Graphics2D graphics, AffineTransform transform ) {
|
||||||
|
if( !visible ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AffineTransform orig = graphics.getTransform();
|
||||||
|
if( transform != null ) {
|
||||||
|
//graphics.transform(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.translate(x, y);
|
||||||
|
graphics.rotate(Math.toRadians(rotation));
|
||||||
|
|
||||||
|
java.awt.Shape shape = getShape();
|
||||||
|
|
||||||
|
java.awt.Color currentColor = graphics.getColor();
|
||||||
|
fillShape(shape, graphics);
|
||||||
|
strokeShape(shape, graphics);
|
||||||
|
graphics.setColor(currentColor);
|
||||||
|
|
||||||
|
graphics.setTransform(orig);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals( Object o ) {
|
public boolean equals( Object o ) {
|
||||||
if( this == o ) return true;
|
if( this == o ) return true;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public class CustomShape extends Shape {
|
|||||||
public CustomShape( double x, double y ) {
|
public CustomShape( double x, double y ) {
|
||||||
super(x, y);
|
super(x, y);
|
||||||
path = new Path2D.Double();
|
path = new Path2D.Double();
|
||||||
|
path.moveTo(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustomShape( CustomShape custom ) {
|
public CustomShape( CustomShape custom ) {
|
||||||
@@ -36,7 +37,7 @@ public class CustomShape extends Shape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void lineTo( double x, double y ) {
|
public void lineTo( double x, double y ) {
|
||||||
path.lineTo(x - x, y - y);
|
path.lineTo(x - this.x, y - this.y);
|
||||||
calculateBounds();
|
calculateBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -404,6 +404,7 @@ public abstract class Shape extends BasicDrawable {
|
|||||||
setStrokeColor(shape.getStrokeColor());
|
setStrokeColor(shape.getStrokeColor());
|
||||||
setStrokeWeight(shape.getStrokeWeight());
|
setStrokeWeight(shape.getStrokeWeight());
|
||||||
setStrokeType(shape.getStrokeType());
|
setStrokeType(shape.getStrokeType());
|
||||||
|
setStrokeJoin(shape.getStrokeJoin());
|
||||||
visible = shape.isVisible();
|
visible = shape.isVisible();
|
||||||
rotation = shape.getRotation();
|
rotation = shape.getRotation();
|
||||||
scale(shape.getScale());
|
scale(shape.getScale());
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class Validator {
|
|||||||
|
|
||||||
public static final <T> T requireNotNull( T obj, Supplier<String> msg ) {
|
public static final <T> T requireNotNull( T obj, Supplier<String> msg ) {
|
||||||
if( obj == null ) {
|
if( obj == null ) {
|
||||||
throw new NullPointerException(msg == null ? "Parameter may nut be null." : msg.get());
|
throw new NullPointerException(msg == null ? "Parameter may not be null." : msg.get());
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package schule.ngb.zm.util.io;
|
package schule.ngb.zm.util.io;
|
||||||
|
|
||||||
import schule.ngb.zm.util.Log;
|
import schule.ngb.zm.util.Log;
|
||||||
|
import schule.ngb.zm.util.Validator;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -11,6 +13,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hilfsklasse, um Textdateien in verschiedenen Formaten einzulesen.
|
* Hilfsklasse, um Textdateien in verschiedenen Formaten einzulesen.
|
||||||
@@ -63,6 +66,9 @@ public final class FileLoader {
|
|||||||
* @return Eine Liste mit den Zeilen der Textdatei.
|
* @return Eine Liste mit den Zeilen der Textdatei.
|
||||||
*/
|
*/
|
||||||
public static List<String> loadLines( String source, Charset charset ) {
|
public static List<String> loadLines( String source, Charset charset ) {
|
||||||
|
Validator.requireNotNull(source, "source");
|
||||||
|
Validator.requireNotNull(charset, "charset");
|
||||||
|
|
||||||
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
@@ -72,8 +78,11 @@ public final class FileLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
} catch( MalformedURLException muex ) {
|
||||||
|
LOG.warn("Could not find resource for <%s>", source);
|
||||||
|
return Collections.emptyList();
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
LOG.error(ex, "Error while loading lines from source <%s>", source);
|
LOG.warn(ex, "Error while loading lines from source <%s>", source);
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,6 +110,9 @@ public final class FileLoader {
|
|||||||
* @return Der Inhalt der Textdatei.
|
* @return Der Inhalt der Textdatei.
|
||||||
*/
|
*/
|
||||||
public static String loadText( String source, Charset charset ) {
|
public static String loadText( String source, Charset charset ) {
|
||||||
|
Validator.requireNotNull(source, "source");
|
||||||
|
Validator.requireNotNull(charset, "charset");
|
||||||
|
|
||||||
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
@@ -110,8 +122,11 @@ public final class FileLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result.toString();
|
return result.toString();
|
||||||
|
} catch( MalformedURLException muex ) {
|
||||||
|
LOG.warn("Could not find resource for <%s>", source);
|
||||||
|
return "";
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
LOG.error(ex, "Error while loading string from source <%s>", source);
|
LOG.warn(ex, "Error while loading string from source <%s>", source);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,34 +181,39 @@ public final class FileLoader {
|
|||||||
).toArray(String[][]::new);
|
).toArray(String[][]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double[][] loadValues( String source, char separator, boolean skipFirst ) {
|
public static double[][] loadValues( String source, String separator, boolean skipFirst ) {
|
||||||
return loadValues(source, separator, skipFirst, UTF8);
|
return loadValues(source, separator, skipFirst, UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt Double-Werte aus einer CSV Datei in ein zweidimensionales Array.
|
* Lädt Double-Werte aus einer Text-Datei in ein zweidimensionales Array.
|
||||||
* <p>
|
* <p>
|
||||||
* Die gelesenen Strings werden mit {@link Double#parseDouble(String)} in
|
* Die Zeilen der Eingabedatei werden anhand der Zeichenkette {@code separator}
|
||||||
* {@code double} umgeformt. Es leigt in der Verantwortung des Nutzers
|
* in einzelne Teile aufgetrennt. {@code separator} wird als regulärer Ausdruck
|
||||||
* sicherzustellen, dass die CSV-Datei auch nur Zahlen enthält, die korrekt
|
* interpretiert (siehe {@link String#split(String)}).
|
||||||
* in {@code double} umgewandelt werden können. Zellen für die die
|
* <p>
|
||||||
* Umwandlung fehlschlägt werden mit 0.0 befüllt.
|
* Jeder Teilstring wird mit {@link Double#parseDouble(String)} in
|
||||||
|
* {@code double} umgeformt. Es liegt in der Verantwortung des Nutzers,
|
||||||
|
* sicherzustellen, dass die Eingabedatei nur Zahlen enthält, die korrekt
|
||||||
|
* in {@code double} umgewandelt werden können. Zellen, für die die
|
||||||
|
* Umwandlung fehlschlägt, werden mit 0.0 befüllt.
|
||||||
* <p>
|
* <p>
|
||||||
* Die Methode unterliegt denselben Einschränkungen wie
|
* Die Methode unterliegt denselben Einschränkungen wie
|
||||||
* {@link #loadCsv(String, char, boolean, Charset)}.
|
* {@link #loadCsv(String, char, boolean, Charset)}.
|
||||||
*
|
*
|
||||||
* @param source Die Quelle der CSV-Daten.
|
* @param source Die Quelle der CSV-Daten.
|
||||||
* @param separator Das verwendete Trennzeichen.
|
* @param separator Ein Trennzeichen oder ein regulärer Ausdruck.
|
||||||
* @param skipFirst Ob die erste Zeile übersprungen werden soll.
|
* @param skipFirst Ob die erste Zeile übersprungen werden soll.
|
||||||
* @param charset Die zu verwendende Zeichenkodierung.
|
* @param charset Die zu verwendende Zeichenkodierung.
|
||||||
* @return Ein Array mit den Daten als {@code String}s.
|
* @return Ein Array mit den Daten als {@code String}s.
|
||||||
*/
|
*/
|
||||||
public static double[][] loadValues( String source, char separator, boolean skipFirst, Charset charset ) {
|
public static double[][] loadValues( String source, String separator, boolean skipFirst, Charset charset ) {
|
||||||
int n = skipFirst ? 1 : 0;
|
int n = skipFirst ? 1 : 0;
|
||||||
List<String> lines = loadLines(source, charset);
|
List<String> lines = loadLines(source, charset);
|
||||||
return lines.stream().skip(n).map(
|
return lines.stream().skip(n).map(
|
||||||
( line ) -> Arrays
|
( line ) -> Arrays
|
||||||
.stream(line.split(Character.toString(separator)))
|
//.stream(line.split(Character.toString(separator)))
|
||||||
|
.stream(line.split(separator))
|
||||||
.mapToDouble(
|
.mapToDouble(
|
||||||
( value ) -> {
|
( value ) -> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.awt.Image;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@@ -62,8 +63,8 @@ public class FontLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Font loadFont( String name, String source ) {
|
public static Font loadFont( String name, String source ) {
|
||||||
Validator.requireNotNull(source, "Font source may not be null");
|
Validator.requireNotNull(source, "source");
|
||||||
Validator.requireNotEmpty(source, "Font source may not be empty.");
|
Validator.requireNotEmpty(source, "source");
|
||||||
|
|
||||||
if( fontCache.containsKey(name) ) {
|
if( fontCache.containsKey(name) ) {
|
||||||
LOG.trace("Retrieved font <%s> from font cache.", name);
|
LOG.trace("Retrieved font <%s> from font cache.", name);
|
||||||
@@ -91,10 +92,12 @@ public class FontLoader {
|
|||||||
//ge.registerFont(font);
|
//ge.registerFont(font);
|
||||||
}
|
}
|
||||||
LOG.debug("Loaded custom font from source <%s>.", source);
|
LOG.debug("Loaded custom font from source <%s>.", source);
|
||||||
|
} catch( MalformedURLException muex ) {
|
||||||
|
LOG.warn("Could not find font resource for <%s>", source);
|
||||||
} catch( IOException ioex ) {
|
} catch( IOException ioex ) {
|
||||||
LOG.error(ioex, "Error loading custom font file from source <%s>.", source);
|
LOG.warn(ioex, "Error loading custom font file from source <%s>.", source);
|
||||||
} catch( FontFormatException ffex ) {
|
} catch( FontFormatException ffex ) {
|
||||||
LOG.error(ffex, "Error creating custom font from source <%s>.", source);
|
LOG.warn(ffex, "Error creating custom font from source <%s>.", source);
|
||||||
}
|
}
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eine Hilfsklasse mit Klassenmethoden, um Bilder zu laden.
|
* Eine Hilfsklasse mit Klassenmethoden, um Bilder zu laden.
|
||||||
@@ -67,8 +68,8 @@ public final class ImageLoader {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static BufferedImage loadImage( String source, boolean caching ) {
|
public static BufferedImage loadImage( String source, boolean caching ) {
|
||||||
Validator.requireNotNull(source, "Image source may not be null");
|
Validator.requireNotNull(source, "source");
|
||||||
Validator.requireNotEmpty(source, "Image source may not be empty.");
|
Validator.requireNotEmpty(source, "source");
|
||||||
|
|
||||||
if( caching && imageCache.containsKey(source) ) {
|
if( caching && imageCache.containsKey(source) ) {
|
||||||
return imageCache.get(source);
|
return imageCache.get(source);
|
||||||
@@ -84,8 +85,10 @@ public final class ImageLoader {
|
|||||||
if( caching && img != null ) {
|
if( caching && img != null ) {
|
||||||
imageCache.put(source, img);
|
imageCache.put(source, img);
|
||||||
}
|
}
|
||||||
|
} catch( MalformedURLException muex ) {
|
||||||
|
LOG.warn("Could not find image resource for <%s>", source);
|
||||||
} catch( IOException ioex ) {
|
} catch( IOException ioex ) {
|
||||||
LOG.error(ioex, "Error loading image file from source <%s>.", source);
|
LOG.warn(ioex, "Error loading image file from source <%s>.", source);
|
||||||
}
|
}
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
@@ -318,12 +321,8 @@ public final class ImageLoader {
|
|||||||
* @throws IOException Falls es einen Fehler beim Speichern gab.
|
* @throws IOException Falls es einen Fehler beim Speichern gab.
|
||||||
*/
|
*/
|
||||||
public static void saveImage( BufferedImage image, File file, boolean overwriteIfExists ) throws IOException {
|
public static void saveImage( BufferedImage image, File file, boolean overwriteIfExists ) throws IOException {
|
||||||
if( image == null ) {
|
Validator.requireNotNull(image, "image");
|
||||||
throw new NullPointerException("Image may not be <null>.");
|
Validator.requireNotNull(file, "file");
|
||||||
}
|
|
||||||
if( file == null ) {
|
|
||||||
throw new NullPointerException("File may not be <null>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if( file.isFile() ) {
|
if( file.isFile() ) {
|
||||||
// Datei existiert schon
|
// Datei existiert schon
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ public class ResourceStreamProvider {
|
|||||||
* einer bestehenden Ressource.
|
* einer bestehenden Ressource.
|
||||||
*/
|
*/
|
||||||
public static URL getResourceURL( String source ) throws NullPointerException, IllegalArgumentException, IOException {
|
public static URL getResourceURL( String source ) throws NullPointerException, IllegalArgumentException, IOException {
|
||||||
Validator.requireNotNull(source, "Resource source may not be null");
|
Validator.requireNotNull(source, "source");
|
||||||
Validator.requireNotEmpty(source, "Resource source may not be empty.");
|
Validator.requireNotEmpty(source, "source");
|
||||||
|
|
||||||
// Ist source ein valider Dateipfad?
|
// Ist source ein valider Dateipfad?
|
||||||
File file = new File(source);
|
File file = new File(source);
|
||||||
|
|||||||
@@ -252,4 +252,18 @@ class ColorTest {
|
|||||||
void darker() {
|
void darker() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void compare() {
|
||||||
|
assertEquals(1.0, Color.RED.compare(Color.RED), 0.0001);
|
||||||
|
assertEquals(1.0, Color.BLUE.compare(Color.BLUE), 0.0001);
|
||||||
|
assertEquals(1.0, Color.WHITE.compare(Color.WHITE), 0.0001);
|
||||||
|
assertEquals(1.0, Color.BLACK.compare(Color.BLACK), 0.0001);
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals(0.0, Color.BLACK.compare(Color.WHITE), 0.0001);
|
||||||
|
assertEquals(0.0, Color.WHITE.compare(Color.BLACK), 0.0001);
|
||||||
|
|
||||||
|
assertEquals(0.5, Color.GRAY.compare(Color.BLACK), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/test/java/schule/ngb/zm/Testmaschine.java
Normal file
51
src/test/java/schule/ngb/zm/Testmaschine.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package schule.ngb.zm;
|
||||||
|
|
||||||
|
|
||||||
|
import schule.ngb.zm.layers.DrawingLayer;
|
||||||
|
import schule.ngb.zm.util.Log;
|
||||||
|
|
||||||
|
public class Testmaschine extends Zeichenmaschine {
|
||||||
|
|
||||||
|
static {
|
||||||
|
Log.enableGlobalDebugging();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DrawingLayer gridLayer;
|
||||||
|
|
||||||
|
public Testmaschine() {
|
||||||
|
this(400, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Testmaschine( int width, int height ) {
|
||||||
|
super(width, height, "Testmaschine", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void settings() {
|
||||||
|
gridLayer = new DrawingLayer(getWidth(), getHeight());
|
||||||
|
this.getCanvas().addLayer(1, gridLayer);
|
||||||
|
setGrid(50, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGrid( int majorGrid, int minorGrid ) {
|
||||||
|
gridLayer.clear();
|
||||||
|
|
||||||
|
gridLayer.clear(LIGHTGRAY);
|
||||||
|
gridLayer.setStrokeColor(LIGHTGRAY.darker(20));
|
||||||
|
for( int i = 0; i < getWidth(); i += minorGrid ) {
|
||||||
|
gridLayer.line(i, 0, i, gridLayer.getHeight());
|
||||||
|
}
|
||||||
|
for( int i = 0; i < getHeight(); i += minorGrid ) {
|
||||||
|
gridLayer.line(0, i, gridLayer.getWidth(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
gridLayer.setStrokeColor(LIGHTGRAY.darker(50));
|
||||||
|
for( int i = 0; i < getWidth(); i += majorGrid ) {
|
||||||
|
gridLayer.line(i, 0, i, gridLayer.getHeight());
|
||||||
|
}
|
||||||
|
for( int i = 0; i < getHeight(); i += majorGrid ) {
|
||||||
|
gridLayer.line(0, i, gridLayer.getWidth(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
src/test/java/schule/ngb/zm/TestmaschineTest.java
Normal file
71
src/test/java/schule/ngb/zm/TestmaschineTest.java
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package schule.ngb.zm;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import schule.ngb.zm.layers.DrawingLayer;
|
||||||
|
import schule.ngb.zm.util.io.ImageLoader;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static schule.ngb.zm.util.test.ImageAssertions.assertEquals;
|
||||||
|
import static schule.ngb.zm.util.test.ImageAssertions.setSaveDiffImageOnFail;
|
||||||
|
|
||||||
|
public class TestmaschineTest {
|
||||||
|
|
||||||
|
private static Testmaschine tm;
|
||||||
|
|
||||||
|
private static DrawingLayer drawing;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
setSaveDiffImageOnFail(true);
|
||||||
|
|
||||||
|
tm = new Testmaschine();
|
||||||
|
drawing = tm.getDrawingLayer();
|
||||||
|
assertNotNull(drawing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void afterAll() {
|
||||||
|
tm.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
drawing.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSaveDiffImage() {
|
||||||
|
drawing.noStroke();
|
||||||
|
drawing.setAnchor(Constants.NORTHWEST);
|
||||||
|
drawing.setFillColor(Constants.BLUE);
|
||||||
|
drawing.rect(0, 0, 400, 400);
|
||||||
|
drawing.setFillColor(Constants.RED);
|
||||||
|
drawing.rect(100, 100, 200, 200);
|
||||||
|
|
||||||
|
BufferedImage img1 = ImageLoader.createImage(400, 400);
|
||||||
|
Graphics2D graphics = img1.createGraphics();
|
||||||
|
|
||||||
|
graphics.setColor(Constants.BLUE.getJavaColor());
|
||||||
|
graphics.fillRect(0, 0, 400, 400);
|
||||||
|
graphics.setColor(Constants.RED.getJavaColor());
|
||||||
|
graphics.fillRect(100, 100, 200, 200);
|
||||||
|
|
||||||
|
assertEquals(drawing.buffer, drawing.buffer);
|
||||||
|
assertEquals(ImageLoader.copyImage(drawing.buffer), drawing.buffer);
|
||||||
|
assertEquals(img1, drawing.buffer);
|
||||||
|
assertEquals(img1, tm.getImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGrid() {
|
||||||
|
// tm.setGrid(50, 10);
|
||||||
|
tm.delay(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
96
src/test/java/schule/ngb/zm/anim/AnimationGroupsTest.java
Normal file
96
src/test/java/schule/ngb/zm/anim/AnimationGroupsTest.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package schule.ngb.zm.anim;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import schule.ngb.zm.Color;
|
||||||
|
import schule.ngb.zm.Testmaschine;
|
||||||
|
import schule.ngb.zm.Zeichenmaschine;
|
||||||
|
import schule.ngb.zm.layers.ShapesLayer;
|
||||||
|
import schule.ngb.zm.shapes.Circle;
|
||||||
|
import schule.ngb.zm.shapes.Shape;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
class AnimationGroupsTest {
|
||||||
|
|
||||||
|
private static Testmaschine zm;
|
||||||
|
|
||||||
|
private static ShapesLayer shapes;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
zm = new Testmaschine();
|
||||||
|
shapes = zm.getShapesLayer();
|
||||||
|
assertNotNull(shapes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void afterAll() {
|
||||||
|
zm.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
shapes.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void animationGroup() {
|
||||||
|
Shape s = new Circle(0, 0, 10);
|
||||||
|
shapes.add(s);
|
||||||
|
|
||||||
|
Animation<Shape> anims = new AnimationGroup<>(
|
||||||
|
500,
|
||||||
|
Arrays.asList(
|
||||||
|
new MoveAnimation(s, 200, 200, 2000, Easing.DEFAULT_EASING),
|
||||||
|
new FillAnimation(s, Color.GREEN, 1000, Easing.sineIn())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Animations.playAndWait(anims);
|
||||||
|
assertEquals(200, s.getX());
|
||||||
|
assertEquals(200, s.getY());
|
||||||
|
assertEquals(Color.GREEN, s.getFillColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void animationSequence() {
|
||||||
|
Shape s = new Circle(0, 0, 10);
|
||||||
|
shapes.add(s);
|
||||||
|
|
||||||
|
Animation<Shape> anims = new AnimationSequence<>(
|
||||||
|
Arrays.asList(
|
||||||
|
new CircleAnimation(s, 200, 0, 90, false, 1000, Easing::rushIn),
|
||||||
|
new CircleAnimation(s, 200, 400, 90, 1000, Easing::rushOut),
|
||||||
|
new CircleAnimation(s, 200, 400, 90, false, 1000, Easing::rushIn),
|
||||||
|
new CircleAnimation(s, 200, 0, 90, 1000, Easing::rushOut)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Animations.playAndWait(anims);
|
||||||
|
assertEquals(0, s.getX());
|
||||||
|
assertEquals(0, s.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void animationSequenceContinous() {
|
||||||
|
Shape s = new Circle(0, 0, 10);
|
||||||
|
shapes.add(s);
|
||||||
|
|
||||||
|
Animation<Shape> anims = new ContinousAnimation<>(new AnimationSequence<>(
|
||||||
|
Arrays.asList(
|
||||||
|
new CircleAnimation(s, 200, 0, 90, false, 1000, Easing::rushIn),
|
||||||
|
new CircleAnimation(s, 200, 400, 90, 1000, Easing::rushOut),
|
||||||
|
new CircleAnimation(s, 200, 400, 90, false, 1000, Easing::rushIn),
|
||||||
|
new CircleAnimation(s, 200, 0, 90, 1000, Easing::rushOut)
|
||||||
|
)
|
||||||
|
), false);
|
||||||
|
Animations.playAndWait(anims);
|
||||||
|
zm.delay(8000);
|
||||||
|
anims.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
75
src/test/java/schule/ngb/zm/anim/AnimationTest.java
Normal file
75
src/test/java/schule/ngb/zm/anim/AnimationTest.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package schule.ngb.zm.anim;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import schule.ngb.zm.Testmaschine;
|
||||||
|
import schule.ngb.zm.layers.ShapesLayer;
|
||||||
|
import schule.ngb.zm.shapes.Circle;
|
||||||
|
import schule.ngb.zm.shapes.Shape;
|
||||||
|
import schule.ngb.zm.util.test.TestEnv;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class AnimationTest {
|
||||||
|
|
||||||
|
private static Testmaschine zm;
|
||||||
|
|
||||||
|
private static ShapesLayer shapes;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
zm = new Testmaschine();
|
||||||
|
shapes = zm.getShapesLayer();
|
||||||
|
assertNotNull(shapes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void afterAll() {
|
||||||
|
zm.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
shapes.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void circleAnimation() {
|
||||||
|
Shape s = new Circle(zm.getWidth()/4.0, zm.getHeight()/2.0, 10);
|
||||||
|
shapes.add(s);
|
||||||
|
|
||||||
|
CircleAnimation anim = new CircleAnimation(s, zm.getWidth()/2.0, zm.getHeight()/2.0, 360, true, 3000, Easing::linear);
|
||||||
|
Animations.playAndWait(anim);
|
||||||
|
assertEquals(zm.getWidth()/4.0, s.getX());
|
||||||
|
assertEquals(zm.getHeight()/2.0, s.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fadeAnimation() {
|
||||||
|
Shape s = new Circle(zm.getWidth()/4.0, zm.getHeight()/2.0, 10);
|
||||||
|
s.setFillColor(s.getFillColor(), 0);
|
||||||
|
s.setStrokeColor(s.getStrokeColor(), 0);
|
||||||
|
shapes.add(s);
|
||||||
|
|
||||||
|
Animation<Shape> anim = new FadeAnimation(s, 255, 1000);
|
||||||
|
Animations.playAndWait(anim);
|
||||||
|
assertEquals(s.getFillColor().getAlpha(), 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void continousAnimation() {
|
||||||
|
Shape s = new Circle(zm.getWidth()/4, zm.getHeight()/2, 10);
|
||||||
|
shapes.add(s);
|
||||||
|
|
||||||
|
ContinousAnimation anim = new ContinousAnimation(
|
||||||
|
new CircleAnimation(s, zm.getWidth()/2, zm.getHeight()/2, 360, true, 1000, Easing::linear)
|
||||||
|
);
|
||||||
|
Animations.play(anim);
|
||||||
|
zm.delay(3000);
|
||||||
|
anim.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
src/test/java/schule/ngb/zm/layers/DrawingLayerTest.java
Normal file
30
src/test/java/schule/ngb/zm/layers/DrawingLayerTest.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package schule.ngb.zm.layers;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import schule.ngb.zm.Constants;
|
||||||
|
import schule.ngb.zm.Zeichenmaschine;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class DrawingLayerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void imageRotateAndScale() {
|
||||||
|
Zeichenmaschine zm = new Zeichenmaschine();
|
||||||
|
zm.getDrawingLayer().imageRotateAndScale(
|
||||||
|
"WitchCraftIcons_122_t.PNG",
|
||||||
|
50, 100,
|
||||||
|
90,
|
||||||
|
300, 200,
|
||||||
|
Constants.NORTHWEST
|
||||||
|
);
|
||||||
|
zm.redraw();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(4000);
|
||||||
|
} catch( InterruptedException e ) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -72,10 +72,10 @@ class FileLoaderTest {
|
|||||||
{2.1,2.2,2.3},
|
{2.1,2.2,2.3},
|
||||||
{3.1,3.2,3.3}
|
{3.1,3.2,3.3}
|
||||||
};
|
};
|
||||||
csv = FileLoader.loadValues("data_comma.csv", ',', true);
|
csv = FileLoader.loadValues("data_comma.csv", ",", true);
|
||||||
assertArrayEquals(data, csv);
|
assertArrayEquals(data, csv);
|
||||||
|
|
||||||
csv = FileLoader.loadValues("data_semicolon_latin.csv", ';', true, FileLoader.ISO_8859_1);
|
csv = FileLoader.loadValues("data_semicolon_latin.csv", ";", true, FileLoader.ISO_8859_1);
|
||||||
assertArrayEquals(data, csv);
|
assertArrayEquals(data, csv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
165
src/test/java/schule/ngb/zm/util/test/ImageAssertions.java
Normal file
165
src/test/java/schule/ngb/zm/util/test/ImageAssertions.java
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package schule.ngb.zm.util.test;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.opentest4j.AssertionFailedError;
|
||||||
|
import schule.ngb.zm.util.io.ImageLoader;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public final class ImageAssertions {
|
||||||
|
|
||||||
|
private static boolean SAVE_DIFF_IMAGE_ON_FAIL = false;
|
||||||
|
|
||||||
|
private static File DIFF_IMAGE_PATH = new File("build/test-results/diff");
|
||||||
|
|
||||||
|
private static AssertionFailedError ASSERTION_FAILED_ERROR = null;
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isSaveDiffImageOnFail() {
|
||||||
|
return SAVE_DIFF_IMAGE_ON_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void setSaveDiffImageOnFail( boolean saveOnFail ) {
|
||||||
|
SAVE_DIFF_IMAGE_ON_FAIL = saveOnFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getDiffImagePath() {
|
||||||
|
return DIFF_IMAGE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertEquals( BufferedImage expected, BufferedImage actual ) {
|
||||||
|
assertEquals(expected, actual, () -> "Actual image differs from expected buffer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertEquals( BufferedImage expected, BufferedImage actual, String message ) {
|
||||||
|
assertEquals(expected, actual, () -> message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertEquals( BufferedImage expected, BufferedImage actual, Supplier<String> messageSupplier ) {
|
||||||
|
// Compare image dimensions
|
||||||
|
int expectedHeight = expected.getHeight(), expectedWidth = expected.getWidth();
|
||||||
|
int actualHeight = actual.getHeight(), actualWidth = actual.getWidth();
|
||||||
|
try {
|
||||||
|
Assertions.assertEquals(expectedHeight, actualHeight);
|
||||||
|
Assertions.assertEquals(expectedWidth, actualWidth);
|
||||||
|
} catch( AssertionFailedError afe ) {
|
||||||
|
ASSERTION_FAILED_ERROR = afe;
|
||||||
|
fail(expected, actual, messageSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Fix comparison of transparent pixels
|
||||||
|
for( int x = 0; x < actualWidth; x++ ) {
|
||||||
|
for( int y = 0; y < actualHeight; y++ ) {
|
||||||
|
try {
|
||||||
|
Assertions.assertTrue(comparePixels(expected.getRGB(x, y), actual.getRGB(x, y)));
|
||||||
|
} catch( AssertionFailedError afe ) {
|
||||||
|
ASSERTION_FAILED_ERROR = afe;
|
||||||
|
fail(expected, actual, messageSupplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertNotEquals( BufferedImage expected, BufferedImage actual ) {
|
||||||
|
assertNotEquals(expected, actual, () -> "Actual image is the same as expected buffer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertNotEquals( BufferedImage expected, BufferedImage actual, String message ) {
|
||||||
|
assertNotEquals(expected, actual, () -> message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertNotEquals( BufferedImage expected, BufferedImage actual, Supplier<String> messageSupplier ) {
|
||||||
|
// Compare image dimensions
|
||||||
|
int expectedHeight = expected.getHeight(), expectedWidth = expected.getWidth();
|
||||||
|
int actualHeight = actual.getHeight(), actualWidth = actual.getWidth();
|
||||||
|
if( expectedHeight != actualHeight || expectedWidth != actualWidth ) {
|
||||||
|
// Image dimensions differ, assertion is true
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( int x = 0; x < actualWidth; x++ ) {
|
||||||
|
for( int y = 0; y < actualHeight; y++ ) {
|
||||||
|
if( !comparePixels(expected.getRGB(x, y), actual.getRGB(x, y)) ) {
|
||||||
|
// Found different pixels, assertion is true
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Images are the same, fail without diff
|
||||||
|
fail(expected, actual, messageSupplier, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail( BufferedImage expected, BufferedImage actual, Supplier<String> messageSupplier ) {
|
||||||
|
fail(expected, actual, messageSupplier, SAVE_DIFF_IMAGE_ON_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail( BufferedImage expected, BufferedImage actual, Supplier<String> messageSupplier, boolean saveDiffImage ) {
|
||||||
|
if( saveDiffImage ) {
|
||||||
|
saveDiffImage(expected, actual);
|
||||||
|
}
|
||||||
|
throw new AssertionFailedError(
|
||||||
|
messageSupplier != null ? messageSupplier.get() : null,
|
||||||
|
ASSERTION_FAILED_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean comparePixels( int a, int b ) {
|
||||||
|
// TODO: Fix comparison of transparent pixels
|
||||||
|
return a == b || ((0xFF000000 & a) == 0 && (0xFF000000 & b) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage createDiffImage( BufferedImage expected, BufferedImage actual ) {
|
||||||
|
// Error color (white)
|
||||||
|
int errorColor = 0xFF00FF;
|
||||||
|
|
||||||
|
int expectedHeight = expected.getHeight(), expectedWidth = expected.getWidth();
|
||||||
|
int actualHeight = actual.getHeight(), actualWidth = actual.getWidth();
|
||||||
|
int maxHeight = Math.max(expectedHeight, actualHeight), maxWidth = Math.max(expectedWidth, actualWidth);
|
||||||
|
|
||||||
|
BufferedImage diff = ImageLoader.createImage(maxWidth, maxHeight);
|
||||||
|
for( int x = 0; x < maxWidth; x++ ) {
|
||||||
|
for( int y = 0; y < maxHeight; y++ ) {
|
||||||
|
diff.setRGB(x, y, 0);
|
||||||
|
if( x > actualWidth || y > actualHeight || x > expectedWidth || y > expectedHeight ) {
|
||||||
|
// Set overflow pixels to error color
|
||||||
|
diff.setRGB(x, y, errorColor);
|
||||||
|
} else if( !comparePixels(actual.getRGB(x, y), expected.getRGB(x, y)) ) {
|
||||||
|
// Set differences to error color
|
||||||
|
// If both pixels are transparent, the color dows not matter ...
|
||||||
|
// TODO: saturate error color based on how different the colors are?
|
||||||
|
diff.setRGB(x, y, errorColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean saveDiffImage( BufferedImage expected, BufferedImage actual ) {
|
||||||
|
BufferedImage diff = createDiffImage(expected, actual);
|
||||||
|
try {
|
||||||
|
File diffFile = new File(DIFF_IMAGE_PATH, makeDiffName());
|
||||||
|
if( !diffFile.getParentFile().exists() ) {
|
||||||
|
diffFile.mkdirs();
|
||||||
|
}
|
||||||
|
ImageLoader.saveImage(diff, diffFile);
|
||||||
|
} catch( IOException ioe ) {
|
||||||
|
// We fail anyways at this point
|
||||||
|
// TODO: Log something?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String makeDiffName() {
|
||||||
|
return System.currentTimeMillis() + ".png";
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageAssertions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
src/test/java/schule/ngb/zm/util/test/TestEnv.java
Normal file
25
src/test/java/schule/ngb/zm/util/test/TestEnv.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package schule.ngb.zm.util.test;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
import org.junit.jupiter.api.extension.ParameterContext;
|
||||||
|
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||||
|
import org.junit.jupiter.api.extension.ParameterResolver;
|
||||||
|
import schule.ngb.zm.Testmaschine;
|
||||||
|
import schule.ngb.zm.Zeichenmaschine;
|
||||||
|
|
||||||
|
public class TestEnv implements ParameterResolver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsParameter( ParameterContext parameterContext, ExtensionContext extensionContext ) throws ParameterResolutionException {
|
||||||
|
return (
|
||||||
|
parameterContext.getParameter().getType() == Zeichenmaschine.class ||
|
||||||
|
parameterContext.getParameter().getType() == Testmaschine.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object resolveParameter( ParameterContext parameterContext, ExtensionContext extensionContext ) throws ParameterResolutionException {
|
||||||
|
return new Testmaschine();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user