237 Commits

Author SHA1 Message Date
d500c130ed Changelog update 2022-12-10 11:55:26 +01:00
b4d390cd9b Feheler beim buildtask der mp3-jar behoben 2022-12-10 11:55:19 +01:00
97ea610f34 Dokumentation 2022-12-10 11:14:38 +01:00
e2e1f24e3e Verison auf 0.0.34 erhöht 2022-12-10 11:11:26 +01:00
a09b956b48 Dokumentation
Ebene der Unterüberschrift auf h2 angepasst
2022-12-10 11:11:17 +01:00
c92a4517b3 Faker Klasse um Fake-Daten zu generieren 2022-12-10 11:07:54 +01:00
1260a38bb7 Imports bereinigt 2022-12-10 11:07:17 +01:00
20772da813 {@inheritDoc} Kommentare entfernt
Javadoc Kommentare, die nur {@inheritDoc} enthalten, sind redundant und wurden entfernt.
2022-12-10 11:06:51 +01:00
8898d6e8ee MKDocs Dokumentation erweitert 2022-12-10 11:05:09 +01:00
cb0ee9c842 Dokumentation 2022-12-10 11:04:48 +01:00
3cf7871591 Weitere image-Methoden ergänzt
Die neuen Methoden erlauben es, Bilder auch mit einer festen Größe auf die Zeichenebene zu zeichnen.
2022-12-09 18:01:52 +01:00
c7e1eb11ed maxInt Test eingefügt 2022-12-08 21:16:35 +01:00
9fc58b05b6 Dokumentation 2022-12-08 21:16:01 +01:00
8de3c41b9b Dokumentation 2022-12-08 16:09:13 +01:00
15e47ceaa8 Reichenfolge Parameter in addValue an PieChart angepasst 2022-12-08 16:08:59 +01:00
9f28786ab6 AudiListener interface angepasst
Die Listener Methoden haben nun sprechendere Namen.
Sound und Mixer akzeptieren nun auch AudioListener.
2022-12-08 16:08:35 +01:00
18b5c50016 Dokumentation 2022-12-08 12:47:59 +01:00
03945e029a Schnellstart Tutorial fertig 2022-12-08 10:16:22 +01:00
90e043e5f8 Gradle Tasks für source und javadoc jars 2022-12-08 10:16:07 +01:00
559459aef6 Dokumentation 2022-12-08 10:15:35 +01:00
8d0bd2bc99 Race condition beim Beenden behoben
Das Beenden der Zeichenmaschine und vor allem das Schließen des Zeichenfesnters wird im Swing Thread ausgeführt. Es konnte passieren, dass der Zeichenthread noch einen draw-Aufruf verarbeitete, während die Zeichenleinwand schon disposed wurde. Dann konnte eine NullPointerException auftreten.

Der Zeichenthread hat nun 500 ms Zeit, von alleine zu beenden, bevor die ZM vollständig beendet wird.
2022-12-08 10:14:00 +01:00
b76d533739 Erste Seiten mit mkdocs 2022-11-29 10:55:54 +01:00
807a13b725 buildfile cleanup 2022-11-29 10:55:22 +01:00
d4c5dbbb53 Gradle 7.4 -> 7.5 2022-11-29 10:55:10 +01:00
080db1f431 Einige Bugfixes und Verbesserungen und ganz viel Doku 2022-11-29 10:12:14 +01:00
47827683e8 Farbnamen werden nun in Colo-Objekte geparsed
`Color.parseString(String)` liest nun eine Datei mit Farbnamen und Hexcode Kombinationen ein. Wird der String in der Liste der Farbnamen gefunden, wird aus dem entsprechenden Hexcode ein `Color`-Objekt erzeugt.
2022-11-29 10:11:43 +01:00
ec30afd441 Fixed icon loading on windows 2022-11-28 09:26:45 +01:00
d3bdbdbffb Konstanten für Schriften und kleinere fixes 2022-11-28 09:11:37 +01:00
8cc7167d7e Formatierung und Doku 2022-11-28 09:11:13 +01:00
9e4271c304 Laden alternativer Schriften möglich 2022-11-28 09:10:59 +01:00
4f13f5177d Mausposition merken wenn pausiert 2022-11-28 09:10:42 +01:00
6321a7d421 render Method added 2022-11-28 09:10:31 +01:00
135af10729 Python files added 2022-11-28 09:09:58 +01:00
912f68c58f Javadoc 2022-08-01 20:50:23 +02:00
7f1d9012e9 Unter macOS auf Cmd+Q reagieren 2022-08-01 20:50:16 +02:00
60ed045986 Javadoc 2022-08-01 20:49:49 +02:00
dc16608333 Javadoc 2022-08-01 14:48:17 +02:00
7b6398fe52 Einfache Faker-Klasse, um Zufallsdaten zu erzeugen 2022-08-01 14:42:20 +02:00
8f98ddc56d Changelog 2022-08-01 10:08:15 +02:00
782ce33540 GradientPaint durch MultipleGradientPaint ersetzt 2022-08-01 10:08:11 +02:00
537527e525 Versuch den Interrupt von dispose() zu verhindern 2022-08-01 10:07:53 +02:00
8e93866b5e Interfaces verschoben 2022-07-31 10:03:28 +02:00
fcb536ff96 copyFrom angepasst 2022-07-31 10:02:11 +02:00
70c607f2e8 java.io -> java.nio 2022-07-31 10:00:22 +02:00
6126ed3c15 Vereinheitlichung der APIs für Füllungen und Konturen 2022-07-31 09:59:36 +02:00
b0353c53a0 Refactorings 2022-07-28 12:25:56 +02:00
c93a203ab9 DrawingLayer delegiert nun zu einer Shape
Macht Weniger doppelte Implementierungen nötig
2022-07-28 12:25:35 +02:00
f1d32685b4 KeyListener wieder zur Canvas bewegt 2022-07-28 12:24:48 +02:00
91842b511f Refactorings und Javadoc 2022-07-28 12:24:30 +02:00
52b480b46b Refactorings 2022-07-27 20:37:13 +02:00
4d2ade899d Refactorings und Javadoc 2022-07-27 20:37:01 +02:00
dcdca893b7 Refactorings und Javadoc 2022-07-27 20:36:34 +02:00
ebf0135486 Versionsnummer erhöht 2022-07-27 13:57:06 +02:00
fea1083926 Javadoc 2022-07-27 13:56:58 +02:00
250d9d17d3 Neuer Zustand QUITING 2022-07-27 13:56:09 +02:00
2a71243fc6 SuppressWarnings eingefügt 2022-07-27 13:55:28 +02:00
03d37222bf Neue choice() Methoden 2022-07-27 13:55:11 +02:00
687d7d35b7 Verbesserter Vollbildmodus und Trennung GUI / Controller 2022-07-27 13:54:55 +02:00
e2e6f8c291 Bug: Synchronized Methoden verschoben 2022-07-26 18:15:23 +02:00
916a581768 Refactorings 2022-07-26 18:14:59 +02:00
5bb2f75193 Bug: getShapes in ShapeGroup war immer leer 2022-07-26 18:14:50 +02:00
f0e4cd6c80 Refactoring des Beendens der ZM 2022-07-26 18:14:23 +02:00
a228b21c84 Verantwortlichkeiten für Layout und Aufgaben klarer getrennt 2022-07-26 08:59:30 +02:00
68c88ec9ca Merge branch 'main' into zeichenfenster
# Conflicts:
#	src/main/java/schule/ngb/zm/media/Sound.java
2022-07-25 19:07:51 +02:00
0d1dd771dd Logger eingefügt 2022-07-25 19:06:01 +02:00
e995bfc4fe Bug: Spielemaschine blockt nicht mehr nebenläufige Threads 2022-07-25 19:05:54 +02:00
97ff03990a Shape caching entfernt
In Tests konnten keine Geschwindigkeitsvorteile festgestellt werden.
2022-07-25 19:05:28 +02:00
bd2364a8df Laden von Schriftarten mit eigenem Namen möglich 2022-07-25 19:05:04 +02:00
617b915874 Refactorings zur Nebenläufigkeit 2022-07-25 17:45:39 +02:00
7772793e8d Kommentar 2022-07-25 17:44:31 +02:00
bd8c0e37a7 Audio-Methoden synchronisiert 2022-07-25 17:44:22 +02:00
ecbe2b4f6b interpolate zu animate umbenannt
Animation erbt zur Vereinfachung nun auch von Constants und dort gibt es schon eine interpolate Methode.
2022-07-25 17:42:06 +02:00
20fe700756 Rechtschreibung und standard Log-Format 2022-07-25 17:41:18 +02:00
0100a3f574 Methode um mehrere Animationen im ShapesLayer zu starten 2022-07-25 17:41:01 +02:00
aceb79c44f Animate Methode zu play umbenannt 2022-07-25 17:40:42 +02:00
a4e29ccdba Loader KLassen in io Paket verschoben 2022-07-25 17:38:53 +02:00
55014c8eec Klasse Zeichenfenster ausgelagert 2022-07-25 17:35:46 +02:00
4f958cd57c ImageLoaders in io Paket verschoben 2022-07-21 22:01:54 +02:00
04506f6e9c JFrame in eine eigene Klasse ausgelagert 2022-07-21 22:01:38 +02:00
5a27e18634 Javadoc und kleine Refactorings 2022-07-21 21:02:50 +02:00
8b23c658e8 Animator Interface entfernt 2022-07-21 21:02:30 +02:00
1ca13c977a Javadoc 2022-07-21 21:02:10 +02:00
78c93666d0 Javadoc 2022-07-21 21:01:46 +02:00
917eb805c6 Bug: Threadsafety 2022-07-21 21:01:33 +02:00
fddd8d621b flush() nach jeder Log-Nachricht
Der Logger sendet nun nach jedem Log die Nachricht zum OutputStream.
2022-07-21 21:00:55 +02:00
4bf0068051 Icons werden nun in allen Größen geladen
Alle vorhandenen Icons werden geladen und mit Jframe.setIconImages() dem Fenster hinzugefügt. Unter macOS wird nur die Größe 512 geladen und als Dock-Icon gesetzt.
2022-07-21 20:59:28 +02:00
371a962432 Syncronisation des Zeichenthreads mit update/draw über eigenen Zustand
delay() setzt den Zustand auf DELAYED und der Zeichenthread läuft weiter, wenn der update/draw Thread in diesen Zustand wechselt (also delay() aufgerufen wurde). Es wird nicht mehr Thread.getState() geprüft, dies zu unzuverlässi gwar.
2022-07-21 10:54:08 +02:00
99848e47f8 colt abhängigkeit nur für’s kompilieren 2022-07-21 10:52:47 +02:00
f75aaf4b7e Predict-Methode für eine Eingabe 2022-07-21 10:52:19 +02:00
e5c6fa634a Anpassung der Package-Struktur 2022-07-20 17:15:29 +02:00
ccc83414c7 Merge branch 'optional-ml' 2022-07-20 17:09:24 +02:00
16477463d4 java doc und refactorings 2022-07-20 17:09:09 +02:00
d3997561fc Streams durch Schleifen ersetzt
Der Overhead durch die parallelen Streams war zu hoch. Jedenfalls bei den relativ kleinen Matrizen im Test. Bei größeren Matrizen könnte die Parallelität einen Vorteil bringen. Ggf. sollte dies getesett werden und abhängig von der Größe die bestte Methode gewählt werden.
2022-07-19 22:53:46 +02:00
b6b4ffe6a5 Weitere Tests eingefügt und verbessert 2022-07-19 22:52:23 +02:00
bf261b5e9b Colt als optionale Abhängigkeit
DAs Anlernen des NN geht um den Faktor 20 schneller, wenn Colt benutzt wird.
2022-07-19 20:05:37 +02:00
b79f26f51e Matric interface umbenannt 2022-07-19 09:14:00 +02:00
538a8215e6 Userinput wird nach Stopp der ZM weiterverarbeitet 2022-07-19 08:56:00 +02:00
cbda5c3077 Bug: Linearer Farbverlauf wurde nicht korrekt berechnet 2022-07-19 08:55:06 +02:00
2caa528a5e Listeniterationen Threadsafe gemacht 2022-07-18 22:48:28 +02:00
bb50abb7bd Javadoc 2022-07-18 22:48:08 +02:00
38d5f22fb6 Bug: UpdateThreadExecutor blockt nun korrekt den Zeichenthread 2022-07-18 22:47:37 +02:00
d34c60505e Bug: mousePressed wurde nicht ausgelöst 2022-07-18 22:46:48 +02:00
4c8e5c8939 USing Colt library as optional dependency 2022-07-18 11:06:08 +02:00
9a9a714050 Javadoc 2022-07-17 16:38:42 +02:00
f0b064a3d5 Changelog 2022-07-17 15:57:34 +02:00
c922357ab7 Bug behoben: Flackern bei Farbverläufen 2022-07-17 15:57:24 +02:00
17c31a1a03 Bug behoben: delay() funktioniert nun auch nach Stopp der ZM 2022-07-17 15:57:02 +02:00
6551bb75c9 Farbverläufe für Formen und neue Konstantennamen 2022-07-17 15:45:05 +02:00
3931e610c6 Changelog und Versionsnummer 2022-07-17 09:28:11 +02:00
bf14bf14dd Fixewd javadoc 2022-07-17 09:27:56 +02:00
222bc0ff7d Merge branch 'concurrent-frames' 2022-07-17 08:55:13 +02:00
33fb503ab8 animateAndWait methoden 2022-07-17 08:54:47 +02:00
aad53d51d1 FadeAnimation erstellt 2022-07-17 08:54:34 +02:00
d87e455e9d Bug beim Laden von AudioStreams behoben 2022-07-17 08:54:17 +02:00
4fd4aa9a94 update/draw nun in eigenem Thread
update/draw wird nun einmal pro Frame als separater Thread ausgeführt. Falls dabei delay oder eine andere wartende Methode aufgerufen wird, läuft die ZM aber weiter, bis der update/draw Thread wieder aufwacht. Dadurch werden Animationen und andere parallele Prozesse nicht auch geblockt.
2022-07-17 08:53:43 +02:00
7031aa40cc Animationssystem erweitert 2022-07-16 17:01:39 +02:00
c295821d85 Timer und Counter Hilfsklassen 2022-07-16 13:32:03 +02:00
91805f7794 Refactorings FileLoader 2022-07-15 22:27:44 +02:00
22c0547caa Merge branch 'main' into tasks 2022-07-15 21:45:37 +02:00
d443d4d11d Merge branch 'main' into ml 2022-07-15 21:44:58 +02:00
4366726671 Merge branch 'main' into events 2022-07-15 21:40:55 +02:00
6040545274 Tests zum Laden und SPeichern 2022-07-15 19:42:48 +02:00
7b84570d18 Umbennung getResourceStream->getinputStream 2022-07-15 19:42:35 +02:00
b24eec5063 Laden und speichern von Netzen ermöglicht 2022-07-15 19:42:13 +02:00
d5abd4ef68 Biases im NeuronLayer eingeführt 2022-07-15 19:42:01 +02:00
9cd37fdce0 Merge branch 'main' into ml 2022-07-14 23:03:08 +02:00
7e023026ce Klasse FileLoader um Textdateien zu laden 2022-07-14 23:02:26 +02:00
005f8299ac Testklasse für Perlin Noise 2022-07-14 19:27:47 +02:00
763d9eff22 Bugfix beim setzen des StrokeType 2022-07-14 19:27:36 +02:00
b244a6e094 arrange und align Funktionen 2022-07-14 18:04:01 +02:00
4597fb411a DrawingLayer.pixel zeichnet nun direkt in den Puffer 2022-07-14 18:03:23 +02:00
9634d11842 Reihenfolge der Richtungs-Enum geändert 2022-07-14 18:02:48 +02:00
e6882fcbf7 WindowAdapter beendet die ZM nun direkt, und wartet nicht auf den Thread
Bisher hat ein Schließen des Fensters dazu geführt, dass `running = false` gestzt wurde und die Maschine den aktuellen Frame noch beenden konnte. Da ein Frame durch die Verwendung von `delay(int)` aber auch lange dauern kann, hatte man als Nutzer dann den Eindruck, dass der Klick nicht registriert wurde.
Nun beendet das Programm direkt, ruft aber trotzdem vorher `teardown()` und `cleanup()` auf.
2022-07-14 18:02:30 +02:00
c0831688ba Test für Noise-Klasse 2022-07-14 17:59:48 +02:00
1e4b865492 Color refactored 2022-07-14 17:59:32 +02:00
0098621ebe Klasse für Perlin Noise implementiert
Die Klasse erzeugt Zuffalswerte nach dem von Ken Perlin erfundenen „Improved Perlin Noise“ Algorithmus.
2022-07-14 17:59:17 +02:00
949f058b5d Fehlenden Klassenkommentar ergänzt 2022-07-12 23:33:38 +02:00
740bb37279 Klasse Constants kommentiert und refactored 2022-07-12 23:22:35 +02:00
f8550ceae5 Projekt an Gradle Struktur angepasst 2022-07-11 22:12:11 +02:00
99d0e702aa eventDispatch nur nach initialisierung aufrufen 2022-07-11 22:09:00 +02:00
5557030e0d Delayed initialization auch für Music Klasse 2022-07-11 14:41:58 +02:00
855d67c873 AnimationListener 2022-07-11 14:41:45 +02:00
bc791a9dc3 Merge branch 'anim' into events 2022-07-11 14:08:03 +02:00
f79cff18a6 Merge branch 'main' into tasks
# Conflicts:
#	src/main/java/schule/ngb/zm/tasks/DelayedTask.java
#	src/main/java/schule/ngb/zm/tasks/FrameSynchronizedTask.java
#	src/main/java/schule/ngb/zm/tasks/FramerateLimitedTask.java
#	src/main/java/schule/ngb/zm/tasks/RateLimitedTask.java
#	src/main/java/schule/ngb/zm/tasks/Task.java
2022-07-11 14:07:24 +02:00
921e2fb3ef An Gradle Struktur angepasst 2022-07-11 14:06:47 +02:00
944249ce82 Merge branch 'main' into anim
# Conflicts:
#	src/main/java/schule/ngb/zm/tasks/DelayedTask.java
#	src/main/java/schule/ngb/zm/tasks/FrameSynchronizedTask.java
#	src/main/java/schule/ngb/zm/tasks/FramerateLimitedTask.java
#	src/main/java/schule/ngb/zm/tasks/RateLimitedTask.java
#	src/main/java/schule/ngb/zm/tasks/Task.java
2022-07-11 14:05:46 +02:00
e1f01fe620 Auf Gradle Struktur angepasst 2022-07-11 14:03:21 +02:00
9f56b49fb7 Merge branch 'main' into events
# Conflicts:
#	src/main/java/schule/ngb/zm/media/AudioListener.java
#	src/main/java/schule/ngb/zm/media/Mixer.java
#	src/main/java/schule/ngb/zm/media/Music.java
#	src/schule/ngb/zm/media/Sound.java
2022-07-11 14:00:37 +02:00
ec8e5cea91 API angepasst und Javadoc verbessert 2022-07-11 13:53:15 +02:00
6a2adc9d4d Mehr Javadoc Korrekturen 2022-07-11 08:22:47 +02:00
9088e6eceb Merge branch 'main' into ml 2022-07-11 08:19:41 +02:00
2c322eb678 Javadocs angepasst und Fehler behoben 2022-07-11 08:18:52 +02:00
b60ffa9ef3 Projektstruktur an Gradle angepasst 2022-07-10 22:46:55 +02:00
0a72b97d56 Gradle initialisiert 2022-07-10 22:46:27 +02:00
4ea526b239 Erste Implementation eines einfachen neuronalen Netzes
Vorbild zur Implementation: https://github.com/wheresvic/neuralnet
2022-07-10 22:33:21 +02:00
54762cb3e6 enabelGlobalDebugging verbessert 2022-07-09 21:01:56 +02:00
2e5d5d7e83 Debug Nachricht beim shutdown 2022-07-09 21:01:39 +02:00
43c5b9f28e Fehler beim erstellen eines AudioStreams behoben 2022-07-09 21:01:01 +02:00
62f221e18e BlueJ Testprojekt entfernt 2022-07-09 21:00:11 +02:00
b3ff1a507e Tests auf Junit5 angepasst 2022-07-08 16:36:33 +02:00
600a49af64 invokeLater gibt nun auch eine Future zurück 2022-07-08 09:52:03 +02:00
b575c47ab3 Neue Easing funktionen 2022-07-08 08:52:31 +02:00
447accc567 Animationen nutzen FrameSynchronizedTasks 2022-07-08 08:01:53 +02:00
2de37e5501 Merge branch 'tasks' into anim 2022-07-08 08:01:35 +02:00
98a62f35cd initialize und finish methoden für Tasks 2022-07-08 08:01:24 +02:00
a52125aeb2 Merge branch 'tasks' into anim 2022-07-08 07:53:01 +02:00
476545f721 Changelog 2022-07-08 07:52:39 +02:00
a8bbce72a2 DelayedTask wartet selbstständig 2022-07-08 07:52:35 +02:00
ced0aa6842 Synchronisation über einen globalen Monitor 2022-07-08 07:44:49 +02:00
d48b167fb3 Renamed generator to dispatcher 2022-07-08 07:31:37 +02:00
e4818d4f3e Abstraktion für Listener API erstellt
EventGenerator soll als einheitlicher Unterbau zur Umstzung von Listener Patterns dienen. Damit sollen Listener wie AudioListener oder AnimationListener umgesetzt werden.
2022-07-07 21:45:08 +02:00
9ee7c606fe Implementierung verschiedener Task-Typen
Die Tasks erfüllen verschiedene Aufgaben und können vom TaskRunner parallel ausgeführt werden. Ob ein so komplexes Task-Management notwendig ist, bleibt offen.
2022-07-07 21:18:45 +02:00
303b667cbf Tests für Animationen 2022-07-07 15:45:25 +02:00
2f59d29d08 Animationen als eigener Prozess
Die Animations API verwendet Funktionale Aspekte der Java 8 API und erlaubt die Animation beliebiger Objekte, aber ist vor allem auf die `shape.*` Klassen ausgelegt.
2022-07-07 15:44:36 +02:00
fc7ee36367 Implementierung von Easing-Funktionen
Die Implementierungen wurden von https://easings.net übernommen.
2022-07-07 15:42:47 +02:00
3030445dcf Changelog 2022-07-07 09:21:39 +02:00
b6b8263e5e Reference auf TextBox entfernt 2022-07-07 09:20:36 +02:00
49ad98fd82 Namen der Threads gesetzt, für leichteres Debugging 2022-07-07 09:20:01 +02:00
1e552d6bc2 Changelog 2022-07-07 08:08:36 +02:00
4e147586e4 Audio Interface ud Mixer Klasse 2022-07-07 08:08:28 +02:00
5213dbb7e9 Formatierung 2022-07-06 22:18:00 +02:00
ed2c14a143 Dokumentation 2022-07-06 22:17:50 +02:00
f9f1cfed24 LineChart auf ChartValue interface umgestellt 2022-07-06 22:17:42 +02:00
e78ec53a53 Merge branch 'main' into charts 2022-07-06 21:29:58 +02:00
c5dd889406 Refactoring der Package-Struktur 2022-07-06 21:27:52 +02:00
a94194daf1 Beispielprojekte zur ZM entfernt
Die Projekte werden in einem eigenen Repository (zeichenmaschine-examples) neu veröffentlicht.
2022-07-06 21:23:44 +02:00
4ad24fc523 Changelog angepasst 2022-07-06 21:15:47 +02:00
e42b02763a width / height umbenannt, damit Constants nicht überschrieben werden
Dies ist ein generelles Problem. Wenn in Objekten die von Constants erben width und height als Objektvariablen genutzt werden sollen, ist die Größe der Zeichenleinwand nicht mehr direkt abrufbar.

Ggf. sollten die Variablen in Constants spezifischer benannt werden (z.B. `zmwidth` oder `canvaWidth`).
2022-07-06 21:15:23 +02:00
df335c8ff2 Weitere Methoden zum hinzufügen und entfernen von Shapes 2022-07-06 21:13:18 +02:00
28d996859a Versionsnummer angepasst 2022-07-06 21:12:57 +02:00
a400829594 updateGame als globale update Methode der Spielemaschine 2022-07-06 21:12:41 +02:00
b5faf202b8 Klassen Sound und Music zur Audiowiedergabe
Sound spielt kurze Clips (z.B. Soundeffekte) ab.
Music für längere Hintergrundmusik.

MP3s werden nur über die Einbindung der externen Abhängigkeiten jlayer, tritonus-share und mp3spi ermöglicht.
2022-07-06 20:46:49 +02:00
950098110f Eine TaskQueue in der Tasks geplant werden können
Es können Tasks (Runnables) für die geplante Ausführung (z.B. in 100 ms) übergeben werden. Die Tasks werden dann jeweils am Ende eines Frames abgearbeitet. Bei Bedarf auch parallel mit dem TaskRunner.

Das System sollte noch einmal Refactored werden. (Und ggf. auch auf seine Sinnhaftigkeit geprüft werden.)
2022-07-06 20:41:34 +02:00
ffe9bef5fd Synchronisierte EventQueue für InputEvents
Eingaben der Swing Componente werden nun in eine interen EventQueue einsortiert, die einmal pro Frame abgearbeitet wird. Das verhindert Probleme bei der Synchronisierung der ZM mit dem EDT von Swing.

Im Moment werden die Originalen InputEvent gespeichert und verarbeitet. In Zukunft könnte eine eigene Event-Klasse sinnvoll sein, die die Events für die Nutzer vereinfacht (siehe Processing).
2022-07-06 20:39:34 +02:00
81624e3a7a Setter für FPS verhindert das setzen auf weniger als 0 frames 2022-07-06 20:36:27 +02:00
1a26afb1ae Fehlende Imports in der Zeichenmaschine gefixt 2022-07-06 20:33:57 +02:00
264552c8b7 Neue Klasse TaskRunner
TaskRunner ist eine statische Klasse, die einen ThreadPool verwaltet und es anderen KLassen ermöglicht, Parallele Prozesse auszuführen. Die ZM ist grundsätzlich erstmal nicht auf Parallelität ausgelegt, da alle im „GameLoop“ (Zeichenmaschine$Zeichenthread) pro Frame synchron läuft. Einige Aufgaben erfordern aber eigene Therad (z.B. das Abspielen von Musikdateien oder zukünftige Animationen).

Der TaskRunner ist eine erste Verison einer Klasse, die zukünftig in der ZM an Bedeutung gewinnen könnte, wenn Parallelität wichtiger wird (z.B. in der Spielemaschine).
2022-07-06 20:33:10 +02:00
bb330c81b8 Reafactoring des ImageLoader
ImageLoader nutz nun den ResourceStreamProvider und die Logging API
2022-07-06 20:29:50 +02:00
aadec6d973 Refactoring des FontLoader
Das Öffnen von Resourcen Streams wurde in eine eigene Hilfsklasse ausgelagert. FontLoader nutzt nun die neue Logging API.
2022-07-06 20:29:21 +02:00
fb2580eb2c Logging API erstellt, um einheitliches Logging zu vereinfachen 2022-07-06 20:18:25 +02:00
49063c9581 Klasse Validator eingefügt 2022-07-06 20:16:37 +02:00
62dc4190a1 Spelling 2022-07-01 13:05:52 +02:00
a31ec502d4 Möglichkeit beliebige Tasks zu timen
Die ZM kann nun beliebige Runnables mit einem Zeitdelay versehen ausführen. Mitteles `scheduleTask(Runnable, int)` wird das angegebene Runnable nach der angegebene Anzahl Millisekunden ausgeführt.
Tasks werden immer am Ende eines Frames ausgeführt.
2022-07-01 13:05:23 +02:00
0f0b03d8fe Verwendung eigener Schriftarten 2022-07-01 13:00:53 +02:00
57c396c2e4 Verwendung der neuen FontLoader KLasse 2022-07-01 13:00:39 +02:00
bd71bb6619 Hilfsklasse um Schrifarten aus Dateien zu laden 2022-07-01 13:00:09 +02:00
fb88f8a17f Spielemaschine auf neue Standardebenen angepasst 2022-06-30 21:29:38 +02:00
c989e69f9f Behandlung von inputs nun in eigener EventQueue
Tastatur- und Mauseingaben werden nun nicht mehr direkt verarbeitet, sondern in eine interne EventQueue geschoben, die nach dem Aufruf von `draw()` abgearbeitet wird. Die `InputEvent`s werden momentan direkt an die üblichen Listener Methoden weitergegeben. Ggf. ist in Zukunft hier auch ein vereinfachtes Eventsystem (siehe Processing) sinnvoll.
Ist die ZM pausiert, werden Events ohne verzögerung direkt ausgelöst.
2022-06-30 21:29:01 +02:00
3e94f42ed3 Zeichenleinwand wird nur noch mit ColorLayer initialisiert 2022-06-30 21:25:05 +02:00
1b5f46c771 Boids Beispiel angepasst und Bug bei Null-Entfernung behoben 2022-06-30 21:24:10 +02:00
445bc29480 Vector wirft nun eine Exception, wenn durch 0 geteilt wird 2022-06-30 21:22:12 +02:00
62e9f5d0f2 Allgemeiner GraphicsLayer hinzugefügt
Vor allem für Testst hilfreich
2022-06-29 22:35:57 +02:00
4f1bd25ad7 Hehomon Beispiel implementiert
Noch Bugs bei Verwendung des Vollbildmodus vorhanden
2022-06-29 22:35:24 +02:00
ff56e689f8 Test für Formen 2022-06-29 22:34:48 +02:00
a01878286b Klassenmethode zur Berechnung beliebiger Ankerpunkte 2022-06-29 22:34:31 +02:00
010a37fc0e Breite / Höhe von Gruppen darf nicht manuell geändert werden 2022-06-29 22:30:39 +02:00
1a55e11841 Texte haben eine separate fontColor
stroke und fill werden nun auf null gesetzt und als Rahmen bzw Hintergrund gezeichnet, wenn sie gesetzt werden.
2022-06-29 22:30:19 +02:00
67610963f2 Spielemaschine als UNterbau für Spiele mit der ZM 2022-06-29 22:29:42 +02:00
57afc4683f Dokumentaiton verbessert 2022-06-29 22:29:23 +02:00
cb303d2c32 Tastencodes als Konstanten ergänzt 2022-06-29 22:29:12 +02:00
7c0d8458ed Rechtschreibung 2022-06-29 22:28:50 +02:00
9344fc284b Interne Verwendung von Settern
Zur Verbesserung der Überschreibbarkeit.
2022-06-29 22:28:40 +02:00
3bdf0daf79 Änderung der Größe in eigener Methode
Die Größe der Zeichenfläche wird nun in einer eigenen, internen Methode geändert, die von setSize und setFullscreen benutzt wird. Dadurch wird die abhängigkeit der beiden Methoden voneinander aufgehoben.
2022-06-29 22:27:44 +02:00
e411b5025e Dokumentation verbessert 2022-06-29 22:26:18 +02:00
e5c2c0e569 Kleine Änderung 2022-06-21 21:33:39 +02:00
3cbdffaa08 Entfernen von Ebenen von der Leinwand ermöglicht 2022-06-21 21:33:19 +02:00
7293f62a19 Standardverschiebung für Shape#nextTo() 2022-06-21 21:32:56 +02:00
2b20cc0f8e Kleiner Bugfix 2022-06-21 21:32:27 +02:00
83039f0717 Dokumentation 2022-06-21 21:32:20 +02:00
f27fc206af Test für Verschiebungen 2022-06-21 21:32:14 +02:00
7cf8f81d07 Richtungen werden nun als Vektoren ausgedrückt 2022-06-21 21:31:57 +02:00
e2c10ff940 Merge branch 'main' into games 2022-06-20 18:31:44 +02:00
6be5104836 Überschreibare Methode zur Reaktion auf verlassen / aktivieren des Vollbildmodus 2022-06-20 18:31:25 +02:00
b3319e09aa Removed SOund and Fonts 2022-06-20 18:21:37 +02:00
3c46ae4faa Hehomon Beispiel 2022-06-20 18:15:10 +02:00
11cb9f7bff Überflüssige Beispiel entfernt 2022-06-20 17:51:57 +02:00
99fda3c37c Erste Diagrammtypen implementiert 2022-06-19 22:28:09 +02:00
302 changed files with 27826 additions and 9071 deletions

24
.gitignore vendored
View File

@@ -32,3 +32,27 @@ hs_err_pid*
.DS_Store
._*
Thumbs.db
.gradle
**/build/
!src/**/build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties
# Cache of project
.gradletasknamecache
# Python mkdocs
.venv
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

View File

@@ -6,11 +6,65 @@ und diese Projekt folgt [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
## Version 0.0.34
### Added
- `Faker`-Klasse zur Erzeugung von Fake-Daten hinzugefügt.
- Dokumentation unter [zeichenmaschine.xyz](https://zeichenmaschine.xyz) mit
[MkDocs](https://www.mkdocs.org) und [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/).
- Neue `image` methoden im `DrawingLayer`.
### Changed
- `FilledShape` und `StrokedShape` durch `Fillable` und `Strokeable` Interfaces ersetzt.
- `Shape` erweitert nun `BasisDrawable` als abstrakte Grundlage.
- `io` Klassen nutzen nun mehr der `java.nio` Funktionen.
- Package-Struktur angepasst.
## Version 0.0.23
### Added
- System für EventListener.
- `AudioListener` und `AnimationListener` als erste Anwendungsfälle.
- Pakete für Animationen und Maschinelles-Lernen.
- Farbverläufe als Füllung.
### Changed
- `update(double)` und `draw()` werden nun in einem eigenen Thread aufgerufen.
- Die Standardwerte in `Constants` wurden mit dem Prefix `DEFAULT_` benannt (vorher `STD_`).
- Die Standardwerte sind nun nicht mehr `final` und können vom Nutzer manuell gesetzt werden.
## Version 0.0.22
### Added
- Interface `Audio` extrahiert, mit Basisfunktionen von `Sound` und `Music`.
- Klasse `Mixer` steuert mehrere Audio-Objekte gleichzeitig.
- Klasse `tasks.RateLimitedTask`, `tasks.FramerateLimitedTask`, `tasks.FrameSynchronizedTask` und `tasks.DelayedTask`.
### Changed
- Neue Package-Struktur:
- `schule.ngb.zm.media` für Audio-Klassen (und ggf. zukünftig Video).
- `schule.ngb.zm.util.tasks` für alles Rund um Parallelität.
- `Zeichenthread` und `TaskRunner` setzen die Namen der Threads für besseres Debugging.
### Removed
- Beispielprojekte in [eigenes Repository](https://github.com/jneug/zeichenmaschine-examples) verschoben.
## Version 0.0.21
### Added
- Parameter `stop_after_draw` im Konstruktor der `Zeichenmaschine` erlaubt es beim Erstellen festzulegen, ob nach dem ersten Frame die Zeichenmaschine gestoppt wird.
- `Picture.tint(Color)` färbt ein Bild ein.
- `Picture.flip(Options.Direction)` spiegelt ein Bild entlang einer Achse (`LEFT`/`RIGHT` für horizontal, `UP`/`DOWN` für vertikal).
- Abstrakte Klasse `Zeichenobjekt` als einheitliche Oberklasse für Objekte in Projekten. Die Klasse erbt von `Constants` und implementiert `Drawabale` und `Updatable` mit leeren Methoden.
- Klasse `java.util.Validator` übernimmt intern Parametervalidierung.
- Klasse `Log` implementiert eine einfache Logging-API über `java.util.logging`.
- Klasse `TaskRunner` führt parallele Prozesse aus.
- `Zeichenmaschine#scheduleTask(Runnable, int)` führt eine Aufgabe nach einer Wartezeit im Gameloop aus.
- Neue Klasse `util.ResourceStreamProvider` sucht Resourcen und öffnet `InputStream`s.
### Changed
- Objektvariablen der `Zeichenmaschine`, die von Unterklassen genutzt werden sollen, sind nun statisch in `Constants`. Dadurch können auch andere Klasse, die von `Constants` erben ohne Umwege auf diese Werte zugreifen (z.B. `width`/`height` der Zeichenleinwand).
- `ImageLoader` und `FontLoader` wurden überarbeitet.
- Nutzung von `Log`
- Nutzung von `ResourceStreamProvider`
- Verarbeitung von Swing `InputEvent`s in einer eigenen interne EventQueue synchron zur Framerate.

48
build.gradle Normal file
View File

@@ -0,0 +1,48 @@
plugins {
id 'idea'
id 'java-library'
}
group 'schule.ngb'
version '0.0.34-SNAPSHOT'
java {
withSourcesJar()
withJavadocJar()
}
compileJava {
options.release = 11
}
repositories {
mavenCentral()
}
dependencies {
runtimeOnly 'com.googlecode.soundlibs:jlayer:1.0.1.4'
runtimeOnly 'com.googlecode.soundlibs:tritonus-share:0.3.7.4'
runtimeOnly 'com.googlecode.soundlibs:mp3spi:1.9.5.4'
compileOnlyApi 'colt:colt:1.2.0'
//api 'colt:colt:1.2.0'
//api 'net.sourceforge.parallelcolt:parallelcolt:0.10.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
test {
useJUnitPlatform()
}
tasks.register('jarMP3SPI', Jar) {
archiveClassifier = 'all'
duplicatesStrategy = 'exclude'
archivesBaseName = 'zeichenmaschine-mp3spi'
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
}

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -0,0 +1,634 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"type": "rectangle",
"version": 152,
"versionNonce": 1288225375,
"isDeleted": false,
"id": "fxk8rHocjpTteICJMa6n8",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 619.9963325816416,
"y": 186.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"width": 150,
"height": 46,
"seed": 1339263918,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"type": "text",
"id": "JmEBjNProPAgJQZUvMGGa"
},
{
"id": "eryKwAzIMcBMQP0Ybl1Mm",
"type": "arrow"
}
],
"updated": 1670307372550,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 264,
"versionNonce": 1267158257,
"isDeleted": false,
"id": "wZLPkORf755_2Vp0J6I0V",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 566.9963325816416,
"y": 289.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"width": 248,
"height": 50,
"seed": 359801906,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"type": "text",
"id": "9TujIdwDvtinylO3z50y6"
},
{
"id": "eryKwAzIMcBMQP0Ybl1Mm",
"type": "arrow"
},
{
"id": "Zc9GOJ8DsIQYo4WaGvvHy",
"type": "arrow"
}
],
"updated": 1670307379131,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 376,
"versionNonce": 1807861489,
"isDeleted": false,
"id": "290mWFx31fA5smc5FqPUr",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 607.9963325816416,
"y": 392.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "#228be6",
"width": 143,
"height": 50,
"seed": 1948766318,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"type": "text",
"id": "_ISR-LCZm2Hu2G57R_uxN"
},
{
"id": "Zc9GOJ8DsIQYo4WaGvvHy",
"type": "arrow"
},
{
"id": "hUUqTCXva-vZjnBeM-PR3",
"type": "arrow"
}
],
"updated": 1670307888001,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 518,
"versionNonce": 2136415391,
"isDeleted": false,
"id": "kbEG_cCZadugfCPxYedhf",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 614.9963325816416,
"y": 589.9758066195944,
"strokeColor": "#000000",
"backgroundColor": "#228be6",
"width": 131,
"height": 50,
"seed": 97599794,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"type": "text",
"id": "6KjVrNy_dxGXJtntdqrjY"
},
{
"id": "hUUqTCXva-vZjnBeM-PR3",
"type": "arrow"
},
{
"id": "bplSSGA4kyy-Av7nKNK1B",
"type": "arrow"
},
{
"id": "qukSk_W6enSdwERPEPkhZ",
"type": "arrow"
}
],
"updated": 1670307888001,
"link": null,
"locked": false
},
{
"type": "text",
"version": 110,
"versionNonce": 1350901746,
"isDeleted": false,
"id": "JmEBjNProPAgJQZUvMGGa",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 643.9963325816416,
"y": 199.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 102,
"height": 20,
"seed": 1889147762,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1670164406970,
"link": null,
"locked": false,
"fontSize": 16,
"fontFamily": 1,
"text": "new Shapes()",
"baseline": 14,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "fxk8rHocjpTteICJMa6n8",
"originalText": "new Shapes()"
},
{
"type": "text",
"version": 245,
"versionNonce": 1536101230,
"isDeleted": false,
"id": "9TujIdwDvtinylO3z50y6",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 584.9963325816416,
"y": 304.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 212,
"height": 20,
"seed": 1297543150,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1670164433291,
"link": null,
"locked": false,
"fontSize": 16,
"fontFamily": 1,
"text": "super(800, 800, \"Shapes\")",
"baseline": 14,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "wZLPkORf755_2Vp0J6I0V",
"originalText": "super(800, 800, \"Shapes\")"
},
{
"type": "text",
"version": 362,
"versionNonce": 2069822514,
"isDeleted": false,
"id": "_ISR-LCZm2Hu2G57R_uxN",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 650.4963325816416,
"y": 407.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 58,
"height": 20,
"seed": 525219186,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1670164509204,
"link": null,
"locked": false,
"fontSize": 16,
"fontFamily": 1,
"text": "setup()",
"baseline": 14,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "290mWFx31fA5smc5FqPUr",
"originalText": "setup()"
},
{
"type": "text",
"version": 502,
"versionNonce": 747611665,
"isDeleted": false,
"id": "6KjVrNy_dxGXJtntdqrjY",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 655.4963325816416,
"y": 604.9758066195944,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 50,
"height": 20,
"seed": 1808016110,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1670307245844,
"link": null,
"locked": false,
"fontSize": 16,
"fontFamily": 1,
"text": "draw()",
"baseline": 14,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "kbEG_cCZadugfCPxYedhf",
"originalText": "draw()"
},
{
"type": "arrow",
"version": 31,
"versionNonce": 101778865,
"isDeleted": false,
"id": "eryKwAzIMcBMQP0Ybl1Mm",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 694.9963325816416,
"y": 242.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "#12b886",
"width": 0,
"height": 38,
"seed": 1130444978,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1670307364549,
"link": null,
"locked": false,
"startBinding": {
"elementId": "fxk8rHocjpTteICJMa6n8",
"focus": 0,
"gap": 10
},
"endBinding": {
"elementId": "wZLPkORf755_2Vp0J6I0V",
"focus": 0.03225806451612903,
"gap": 9
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
0,
38
]
]
},
{
"type": "arrow",
"version": 160,
"versionNonce": 1672489439,
"isDeleted": false,
"id": "Zc9GOJ8DsIQYo4WaGvvHy",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 686.7253597961584,
"y": 347.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "#12b886",
"width": 0.3256260532492661,
"height": 36,
"seed": 1138446962,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1670307364549,
"link": null,
"locked": false,
"startBinding": {
"elementId": "wZLPkORf755_2Vp0J6I0V",
"gap": 8,
"focus": 0.03595554587056003
},
"endBinding": {
"elementId": "290mWFx31fA5smc5FqPUr",
"gap": 9,
"focus": 0.09195903246894747
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-0.3256260532492661,
36
]
]
},
{
"type": "arrow",
"version": 616,
"versionNonce": 447594705,
"isDeleted": false,
"id": "hUUqTCXva-vZjnBeM-PR3",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 684.6524492354905,
"y": 452.97580661959455,
"strokeColor": "#000000",
"backgroundColor": "#228be6",
"width": 1.5815106831711319,
"height": 29.000000000000114,
"seed": 1169930546,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1670307888001,
"link": null,
"locked": false,
"startBinding": {
"elementId": "290mWFx31fA5smc5FqPUr",
"focus": -0.044553538542618336,
"gap": 10
},
"endBinding": {
"elementId": "5cG3FjQlYuIGPZMsIMgJU",
"focus": 0.08947713014192164,
"gap": 6.999999999999915
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
1.5815106831711319,
29.000000000000114
]
]
},
{
"type": "rectangle",
"version": 560,
"versionNonce": 11022527,
"isDeleted": false,
"id": "5cG3FjQlYuIGPZMsIMgJU",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 616.4963325816416,
"y": 488.9758066195945,
"strokeColor": "#000000",
"backgroundColor": "#228be6",
"width": 131,
"height": 50,
"seed": 609895569,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"type": "text",
"id": "-Fl205ZvyGaxHAHt7C3r3"
},
{
"id": "hUUqTCXva-vZjnBeM-PR3",
"type": "arrow"
},
{
"id": "bplSSGA4kyy-Av7nKNK1B",
"type": "arrow"
},
{
"id": "qukSk_W6enSdwERPEPkhZ",
"type": "arrow"
}
],
"updated": 1670307888001,
"link": null,
"locked": false
},
{
"type": "text",
"version": 551,
"versionNonce": 685087263,
"isDeleted": false,
"id": "-Fl205ZvyGaxHAHt7C3r3",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 647.4963325816416,
"y": 503.97580661959455,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 69,
"height": 20,
"seed": 415680767,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": null,
"updated": 1670307260196,
"link": null,
"locked": false,
"fontSize": 16,
"fontFamily": 1,
"text": "update()",
"baseline": 14,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "5cG3FjQlYuIGPZMsIMgJU",
"originalText": "update()"
},
{
"id": "bplSSGA4kyy-Av7nKNK1B",
"type": "arrow",
"x": 683.9963325816416,
"y": 546.9758066195944,
"width": 1,
"height": 34,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#228be6",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 1937189809,
"version": 33,
"versionNonce": 1639939761,
"isDeleted": false,
"boundElements": null,
"updated": 1670307888001,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-1,
34
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "5cG3FjQlYuIGPZMsIMgJU",
"focus": -0.044849023090586096,
"gap": 7.999999999999858
},
"endBinding": {
"elementId": "kbEG_cCZadugfCPxYedhf",
"focus": 0.022646536412078155,
"gap": 9
},
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"id": "qukSk_W6enSdwERPEPkhZ",
"type": "arrow",
"x": 758.9963325816416,
"y": 615.9758066195944,
"width": 87,
"height": 107.99999999999994,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 677436529,
"version": 170,
"versionNonce": 377207327,
"isDeleted": false,
"boundElements": [],
"updated": 1670307315516,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
86,
0
],
[
83,
-106.99999999999994
],
[
-1,
-107.99999999999994
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "kbEG_cCZadugfCPxYedhf",
"focus": 0.04,
"gap": 13
},
"endBinding": {
"elementId": "5cG3FjQlYuIGPZMsIMgJU",
"focus": -0.26783652736088875,
"gap": 10.5
},
"startArrowhead": null,
"endArrowhead": "arrow"
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
},
"files": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

View File

@@ -0,0 +1,375 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "Mhp-wQ2wZxCI4BYWvTvV2",
"type": "ellipse",
"x": 420,
"y": 233,
"width": 385.99999999999994,
"height": 385.99999999999994,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#ffdf22",
"fillStyle": "cross-hatch",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 474299186,
"version": 149,
"versionNonce": 2072559726,
"isDeleted": false,
"boundElements": null,
"updated": 1670158627399,
"link": null,
"locked": false
},
{
"id": "SqZRA75ACKHo0lB799wpS",
"type": "line",
"x": 605,
"y": 426,
"width": 173.02018127597637,
"height": 99.89324823492274,
"angle": 0,
"strokeColor": "#087f5b",
"backgroundColor": "#ffdf22",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 484805038,
"version": 286,
"versionNonce": 1603007154,
"isDeleted": false,
"boundElements": null,
"updated": 1670159044301,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
173.02018127597637,
-99.89324823492274
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "7XVC5Wqiy62pN9Vc92bgl",
"type": "text",
"x": 645.7358370304399,
"y": 356.14958623820286,
"width": 86,
"height": 20,
"angle": 5.766085793504818,
"strokeColor": "#087f5b",
"backgroundColor": "#ffdf22",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 1883808110,
"version": 228,
"versionNonce": 627525998,
"isDeleted": false,
"boundElements": null,
"updated": 1670159044301,
"link": null,
"locked": false,
"text": "moleRadius",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "moleRadius"
},
{
"id": "zp9JMFpfOA6ZEWCCiqwW0",
"type": "line",
"x": 606,
"y": 427,
"width": 128.012747010704,
"height": 208.02230726873202,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 224372978,
"version": 81,
"versionNonce": 563971630,
"isDeleted": false,
"boundElements": null,
"updated": 1670158772553,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-128.012747010704,
-208.02230726873202
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "LIDFlKBUhkbZdzTUFT1qp",
"type": "line",
"x": 604.8240237554426,
"y": 425.10629955596494,
"width": 127.71248741178238,
"height": 4.628286654564533,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 34525358,
"version": 124,
"versionNonce": 1131380590,
"isDeleted": false,
"boundElements": null,
"updated": 1670158787752,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-127.71248741178238,
-4.628286654564533
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "-Of3RfHAvJDq0qHIwjm2U",
"type": "ellipse",
"x": 471.96813247323996,
"y": 413.60866976111646,
"width": 12,
"height": 12,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 1050702514,
"version": 384,
"versionNonce": 1957432558,
"isDeleted": false,
"boundElements": null,
"updated": 1670158970885,
"link": null,
"locked": false
},
{
"id": "NB4WGe-lT7XqLrsKX8jN8",
"type": "ellipse",
"x": 472,
"y": 212,
"width": 12,
"height": 12,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 2059751730,
"version": 277,
"versionNonce": 756441010,
"isDeleted": false,
"boundElements": null,
"updated": 1670158769939,
"link": null,
"locked": false
},
{
"id": "T1vEWEHV-1FM4A7Q7dTzy",
"type": "ellipse",
"x": 601.6117120882632,
"y": 422.28007605476614,
"width": 6.436664554034337,
"height": 6.436664554034337,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#000000",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 1983845742,
"version": 376,
"versionNonce": 1202556466,
"isDeleted": false,
"boundElements": null,
"updated": 1670158984089,
"link": null,
"locked": false
},
{
"id": "eEMIGJ2Ag5HqWZLPmjXMM",
"type": "text",
"x": 551.4695946462369,
"y": 432.8553279772536,
"width": 108,
"height": 20,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#000000",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 817794290,
"version": 97,
"versionNonce": 1438641266,
"isDeleted": false,
"boundElements": null,
"updated": 1670158980419,
"link": null,
"locked": false,
"text": "(moleX, moleY)",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "(moleX, moleY)"
},
{
"id": "6cMmrU3lcJbub8sEKnVWe",
"type": "text",
"x": 409.80838527586974,
"y": 186.48331271464983,
"width": 135,
"height": 20,
"angle": 0,
"strokeColor": "#c92a2a",
"backgroundColor": "#000000",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 399694958,
"version": 166,
"versionNonce": 1571185842,
"isDeleted": false,
"boundElements": null,
"updated": 1670158939920,
"link": null,
"locked": false,
"text": "(mouseX, mouseY)",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "(mouseX, mouseY)"
},
{
"id": "0idmv_zLtEYyctHB4lSWV",
"type": "text",
"x": 406.7568976895149,
"y": 390.3911282833501,
"width": 135,
"height": 20,
"angle": 0,
"strokeColor": "#364fc7",
"backgroundColor": "#000000",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 0,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 1008210350,
"version": 224,
"versionNonce": 968376110,
"isDeleted": false,
"boundElements": null,
"updated": 1670158975919,
"link": null,
"locked": false,
"text": "(mouseX, mouseY)",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "(mouseX, mouseY)"
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
},
"files": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 KiB

View File

@@ -0,0 +1,375 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "HPGP8tbAPy0aGI3_MCE3D",
"type": "diamond",
"x": 491.42779164474496,
"y": 479.56091158550765,
"width": 432.03244941244884,
"height": 55.325188818463005,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 1103578030,
"version": 138,
"versionNonce": 1150893294,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false
},
{
"id": "vhIFJN3-91oJXV-RfFsBf",
"type": "diamond",
"x": 491.42779164474496,
"y": 435.8942449188411,
"width": 432.03244941244884,
"height": 55.325188818463005,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 618886834,
"version": 198,
"versionNonce": 1515704562,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false
},
{
"id": "B2pM-4m_3Dk2w_6dY-hVJ",
"type": "diamond",
"x": 491.42779164474496,
"y": 392.2275782521744,
"width": 432.03244941244884,
"height": 55.325188818463005,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#228be6",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 844345582,
"version": 231,
"versionNonce": 1954508590,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false
},
{
"id": "Ge28XC9PqD26hknNaFXKP",
"type": "diamond",
"x": 491.42779164474496,
"y": 348.5609115855077,
"width": 432.03244941244884,
"height": 55.325188818463005,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 1326657902,
"version": 249,
"versionNonce": 685040306,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false
},
{
"id": "PLU80s2TkyMEAOVgolwJx",
"type": "line",
"x": 506.9963325816415,
"y": 507.9758066195945,
"width": 0,
"height": 132.00000000000006,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 1246804466,
"version": 39,
"versionNonce": 627399022,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
0,
-132.00000000000006
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "xDdJ5FnbMLIbN95FE3Iyc",
"type": "line",
"x": 910.9963325816416,
"y": 506.9758066195945,
"width": 0,
"height": 132,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 1034568050,
"version": 77,
"versionNonce": 1363786866,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
0,
-132
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "clYu5Q9GzKbAfohBEsGwn",
"type": "line",
"x": 709.9963325816416,
"y": 532.9758066195944,
"width": 0,
"height": 129.99999999999994,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 1798035246,
"version": 42,
"versionNonce": 244579246,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
0,
-129.99999999999994
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "v2nD7_bvWJYHqs82XDWO2",
"type": "text",
"x": 935.9963325816416,
"y": 497.2235059947391,
"width": 159,
"height": 20,
"angle": 0,
"strokeColor": "#343a40",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 1323260978,
"version": 111,
"versionNonce": 372528622,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false,
"text": "Ebene 0: ColorLayer",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "Ebene 0: ColorLayer"
},
{
"id": "c6T2j8hozL3RZcNYGMgRl",
"type": "text",
"x": 935.9963325816416,
"y": 453.55683932807256,
"width": 172,
"height": 20,
"angle": 0,
"strokeColor": "#e67700",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 1949746930,
"version": 111,
"versionNonce": 1448527858,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false,
"text": "Ebene 1: DrawingLayer",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "Ebene 1: DrawingLayer"
},
{
"id": "bAmGMg2i4Hvv7t_obXSKn",
"type": "text",
"x": 935.9963325816416,
"y": 409.8901726614059,
"width": 174,
"height": 20,
"angle": 0,
"strokeColor": "#1864ab",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 706529710,
"version": 144,
"versionNonce": 1968138286,
"isDeleted": false,
"boundElements": null,
"updated": 1670163691142,
"link": null,
"locked": false,
"text": "Ebene 2: ShapesLayer",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "Ebene 2: ShapesLayer"
},
{
"id": "9-QieQt-nVqGCe5D4r15j",
"type": "text",
"x": 935.9963325816416,
"y": 366.2235059947392,
"width": 65,
"height": 20,
"angle": 0,
"strokeColor": "#2b8a3e",
"backgroundColor": "#40c057",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 2100742126,
"version": 179,
"versionNonce": 289074606,
"isDeleted": false,
"boundElements": null,
"updated": 1670163697684,
"link": null,
"locked": false,
"text": "Ebene 3",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 14,
"containerId": null,
"originalText": "Ebene 3"
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
},
"files": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

12
docs/assets/zmstyles.css Normal file
View File

@@ -0,0 +1,12 @@
h1.title {
text-align: center;
color: #363636;
margin-bottom: .25rem;
}
h2.subtitle {
text-align: center;
font-size: 1rem;
color: #4a4a4a;
margin-top: -.25rem;
margin-bottom: -1.25rem;
}

43
docs/einfuehrung.md Normal file
View File

@@ -0,0 +1,43 @@
<figure markdown>
![Zeichenmaschine.xyz](assets/icon_512.png){ width=128 }
</figure>
<h1 class="title">Zeichenmaschine.xyz</h1>
<h2 class="subtitle">Eine kleine Java-Bibliothek für grafische Programmierung im
Informatikunterricht.</h2>
## Projektidee
Die **Zeichenmaschine** ist eine für den Informatikunterricht entwickelte Bibliothek,
die unter anderem an [Processing](https://processing.org/) angelehnt ist. Die
Bibliothek soll einige der üblichen Anfängerschwierigkeiten mit Java vereinfachen
und für Schülerinnen und Schüler im Unterricht nutzbar machen.
!!! warning
Das Projekt befindet sich noch in der Entwicklungsphase und auch wenn die
aktuelle Version schon funktionsfähig ist und einen Großteil der angestrebten
Funktionen enthält, ist noch keine stabile Version 1.0 erreicht. Vor allem
am Umfang und konsistenten Design der APIs gilt es noch zu arbeiten und es
können sich Änderungen ergeben.
Feedback und Vorschläge zu diesem Prozess (oder auch eine Beteiligung an der
Entwicklung) können sehr gerne über [Github](https://github.com/jneug) oder
[Mastodon](https://bildung.social/@ngb) an mich kommuniziert werden.
(Gleiches gilt für diese Webseite zum Projekt.)
## Dokumentation
* [Schnellstart](quickstart.md)
* [Installation](installation.md)
* {{ javadoc_link() }}
## Über die Zeichenmaschine
!!! info
In der Zeichenmaschine werden bewusst nur englischsprachige Bezeichner für
Klassen, Methoden und Variablen verwendet. Ausnahme sind einzelne Klassen,
die im Zusammnehang mit dem Namen der Bibliothek stehen, wie die
Hauptklasse `Zeichenmaschine`.

View File

@@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block tabs %}
{{ super() }}
{% endblock %}
{% block content %}{% endblock %}
{% block footer %}{% endblock %}

46
docs/installation.md Normal file
View File

@@ -0,0 +1,46 @@
# Installation
Um ein einfaches Projekt mit der **Zeichenmaschine** aufzusetzen ist nicht mehr
nötig, als
die [JAR-Datei der aktuellen Version](https://github.com/jneug/zeichenmaschine/release/latest)
herunterzuladen und dem *Classpath* des Projekts hinzuzufügen. Beschreibungen
für
verschiedene Entwicklungsumgebungen sind hier aufgelistet.
## Integration in Entwicklungsumgebungen
### BlueJ
[BlueJ](https://bluej.org) sucht an drei Orten nach Bibliotheken, die für ein
Projekt in den Classpath aufgenommen werden:
- Für ein einzelnes Projekt im Projektordner im Unterordner `+libs`.
- Im Reiter "Bibliotheken" der BlueJ-Einstellungen.
Hier können Programmbibliotheken hinzugefügt werden, die dann allen Projekten
zur Verfügung stehen.
- Für alle Projekte und alle Nutzer dieser BlueJ-Version im
Unterordner `userlib` des Programmordners.
Auf Windows-Systemen ist dieser im Order `lib` des Installationsordners von BlueJ zu finden.
Auf macos-Systemen muss via Rechtsklick auf die Programmdatei `BlueJ.app` über den Menüpunkt "Paketinhalt zeigen" in den Ordner `Contents/Resources/Java/` navigiert werden.
### VSCode / VSCodium
> Coming soon
### IntelliJ
> Coming soon
### Eclipse
> Coming soon
### NetBeans
> Coming soon
## Unterstützung für MP3

128
docs/macros.py Normal file
View File

@@ -0,0 +1,128 @@
import re
from typing import List
def define_env(env):
@env.macro
def javadoc(clazz: str = None, target: str = None) -> str:
if not "javadoc_url" in env.variables:
return clazz
if not clazz:
return f"{env.variables['javadoc_url'].rstrip('/')}/index.html"
else:
if "javadoc_default_package" in env.variables and not clazz.startswith(env.variables['javadoc_default_package']):
clazz = f"{env.variables['javadoc_default_package'].rstrip('.')}.{clazz}"
javadoc_url = env.variables["javadoc_url"].rstrip("/")
path = list()
name = list()
for p in clazz.split('.'):
if p[0].islower():
path.append(p)
else:
name.append(p)
path = '/'.join(path) + '/' + '.'.join(name) + ".html"
if target:
path = f"{path}#{target}"
return f"{javadoc_url}/{path}"
@env.macro
def jd(cl: str = None, t: str = None) -> str:
return javadoc(cl, t)
@env.macro
def javadoc_link(
clazz: str = None,
target: str = None,
strip_package: bool = True,
strip_clazz: bool = False,
strip_params: bool = True,
title: str = None
) -> str:
name = clazz or "Javadoc"
if strip_package:
if clazz and clazz.rfind(".") > -1:
name = clazz[clazz.rfind(".") + 1 :]
if target:
# _target = re.sub(r"([^(][^,]*?\.)*?([^)]+)", lambda m: m.group(2), target)
_target = target
if m := re.match(r'^(.+?)\((.*)\)$', _target):
if strip_params and m.group(2):
params = m.group(2).split(',')
for i, param in enumerate(params):
dot = param.rfind('.')
if dot >= 0:
params[i] = param[dot+1:].strip()
params = ", ".join(params)
_target = f'{m.group(1)}({params})'
if strip_clazz:
name = _target
else:
name = f"{name}.{_target}"
if title:
name = title
return f"[`{name}`]({javadoc(clazz, target)})"
@env.macro
def jdl(
cl: str = None,
t: str = None,
p: bool = False,
c: bool = True,
title: str = None
) -> str:
return javadoc_link(cl, t, strip_package=not p, strip_clazz=not c, strip_params=True, title=title)
@env.macro
def jdc(
cl: str,
p: bool = False
) -> str:
return javadoc_link(cl, strip_package=not p)
@env.macro
def jdm(
cl: str,
t: str,
p: bool = False,
c: bool = False
) -> str:
return javadoc_link(cl, t, strip_package=not p, strip_clazz=not c)
@env.macro
def javadoc_signature(
clazz: str = None,
member: str = None,
package: str = None,
params: List[str] = list(),
) -> str:
sig = clazz or ""
if clazz and package:
sig = f"{package}.{sig}"
if member:
sig = f"{sig}#{member}"
pparams = ",".join(params)
sig = f"{sig}({pparams})"
return sig
@env.macro
def jds(
cl: str = None,
m: str = None,
pkg: str = None,
params: List[str] = list(),
) -> str:
javadoc_signature(cl, m, pkg, params)
# schule/ngb/zm/Zeichenmaschine.html#setCursor(java.awt.Image,int,int)
# schule/ngb/zm/Zeichenmaschine.html#getLayer(java.lang.Class)
# schule/ngb/zm/DrawableLayer.html#add(schule.ngb.zm.Drawable...)

606
docs/quickstart.md Normal file
View File

@@ -0,0 +1,606 @@
# Schnellstart mit der Zeichenmaschine
Um die **Zeichenmaschine** in einem Projekt zu nutzen ist nicht mehr nötig, als
die [JAR-Datei der aktuellen Version](https://github.com/jneug/zeichenmaschine/release/latest)
herunterzuladen und
dem [Classpath](https://www.delftstack.com/de/howto/java/java-classpath-/)
hinzuzufügen. Eine Beschreibung für verschiedene Entwicklungsumgebungen findet
sich im Abschnitt [Installation](installation.md).
## Die Basisklasse
Eine _Zeichenmaschine_ wird immer als Unterklasse von {{ javadoc_link("
schule.ngb.zm.Zeichenmaschine") }} erstellt.
```java
public class Shapes extends Zeichenmaschine {
}
```
Die gezeigte Klasse ist schon eine lauffähige Zeichenmaschine und kann gestartet
werden.
!!! note "main Methode"
Bei einigen Entwicklungsumgebungen muss noch eine `main` Methode erstellt
werden, um die Zeichenmaschine zu starten:
```java
public static void main(String[] args) {
new Shapes();
}
```
Es öffnet sich ein Zeichenfenster in einer vordefinierten Größe. Um die
Abmessungen und den Titel des Fensters zu ändern, legen wir einen Konstruktor
an.
???+ example "Quelltext"
```java
public class Shapes extends Zeichenmaschine {
public Shapes() {
super(800, 800, "Shapes");
}
}
```
Starten wir das Projekt, wird eine Zeichenfläche in der Größe 800-mal-800 Pixel
erstellt und in einem Fenster mit dem Titel „Shapes“ angezeigt.
<figure markdown>
![Shapes 2](assets/quickstart/shapes_2.png){ width=400 }
</figure>
### Formen zeichnen
Eine Zeichenmaschine hat verschiedene Möglichkeiten, Inhalte in das
Zeichenfenster zu zeichnen. Um ein einfaches statisches Bild zu erzeugen,
überschreiben wir die {{ jdl("schule.ngb.zm.Zeichenmaschine", "draw()",
c=False) }} Methode.
???+ example "Quelltext"
```java
public class Shapes extends Zeichenmaschine {
public Shapes() {
super(800, 800, "Shapes");
}
@Override
public void draw() {
background.setColor(BLUE);
drawing.setFillColor(255, 223, 34);
drawing.noStroke();
drawing.circle(400, 400, 100);
}
}
```
Wir sehen einen gelben Kreis (ohne Konturlinie) auf einem blauen Hintergrund.
<figure markdown>
![Shapes 3](assets/quickstart/shapes_3.png){ width=400 }
</figure>
### Vorbereitung der Zeichenfläche
Im Beispiel oben setzen wir die Hintergrundfarbe auf Blau, die Füllfarbe auf
Gelb und deaktivieren die Konturlinie. Wenn diese Einstellungen für alle
Zeichenobjekte gleich bleiben, können wir sie statt in `draw()` auch in die {{
jdl('Zeichenmaschine', 'setup()', c=False) }} Methode schreiben. Diese bereitet
die Zeichenfläche vor dem ersten Zeichnen vor.
???+ example "Quelltext"
```java
public class Shapes extends Zeichenmaschine {
public Shapes() {
super(800, 800, "Shapes");
}
@Override
public void setup() {
background.setColor(BLUE);
drawing.setFillColor(255, 223, 34);
drawing.noStroke();
}
@Override
public void draw() {
for( int i = 0; i < 10; i++ ) {
drawing.circle(
random(0, canvasWidth),
random(0, canvasHeight),
random(50, 200)
);
}
}
}
```
Im Beispiel setzen wir nun die Grundeinstellungen in der `setup()` Methode. In
`draw()` werden zehn gelbe Kreise an Zufallskoordinaten gezeichnet.
<figure markdown>
![Shapes 4](assets/quickstart/shapes_4.1.png){ width=400 }
</figure>
!!! tip ""
Mit {{ jdm("Constants", "canvasWidth") }} und
{{ jdm("Constants", "canvasHeight") }} kannst du in der Zeichenmaschine
auf die aktuelle Größe der Zeichenfläche zugreifen.
{{ jdm("Constants", "random(int,int)") }} erzeugt eine Zufallszahl
innerhalb der angegebenen Grenzen.
## Interaktionen mit der Maus: Whack-a-mole
Mit der Zeichenmaschine lassen sich Interaktionen mit der Maus leicht umsetzen.
Wor wollen das Beispielprogramm zu einem
[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
angezeigt. Sobald die Spieler:in auf den Kreis klickt, soll dieser an eine neue
Position springen.
Damit wir den Kreis an eine neue Position springen lassen können, müssen wir
zufällige `x`- und `y`-Koordinaten generieren. Dazu erstellen wir zunächst zwei
_Objektvariablen_ für die Koordinaten, die in der `setup()` Methode mit
zufälligen Werte initialisiert werden. Diese benutzen wir, um die `draw
()` Methode anzupassen.
??? example "Quelltext"
```Java
public class Shapes extends Zeichenmaschine {
private int moleRadius = 20;
private int moleX;
private int moleY;
public Shapes() {
super(800, 800, "Shapes");
}
@Override
public void setup() {
background.setColor(BLUE);
drawing.setFillColor(255, 223, 34);
drawing.noStroke();
moleX = random(50, canvasWidth - 50);
moleY = random(50, canvasHeight - 50);
}
@Override
public void draw() {
drawing.clear();
drawing.circle(moleX, moleY, moleRadius);
}
}
```
<figure markdown>
![Shapes 5](assets/quickstart/shapes_5.1.png){ width=600 }
</figure>
Als Nächstes prüfen wir bei jedem Mausklick, ob die Mauskoordinaten innerhalb
des gelben Kreises (des Maulwurfs) liegen. Die Mauskoordinaten sind jederzeit
über die Variablen `mouseX` und `mouseY` abrufbar. Um zu prüfen, ob diese
Koordinaten innerhalb des Kreises liegen, vergleichen wir den Abstand zwischen
Kreismittelpunkt `(moleX, moleY)` und den Mauskoordinaten
`(mouseX, mouseY)` mit dem Radius des Kreises (im Bild grün). Ist die Entfernung
kleiner als der Radius (blauer Kreis), wurde innerhalb des Kreises geklickt.
Sonst außerhalb (roter Kreis).
<figure markdown>
![Kollision Maus mit Kreis](assets/quickstart/CircleMouseCollision.png){ width=400 }
</figure>
Den Abstand vom Mittelpunkt zur Maus lässt sich mithilfe des Satzes des
Pythagoras leicht selber berechnen. Die Zeichenmaschine kann uns diese Arbeit
aber auch abnehmen und stellt eine Methode dafür bereit
({{ jdm("Constants", "distance(double,double,double,double)") }}).
Um auf einen Mausklick zu reagieren, ergänzen wir die
{{ jdm("Zeichenmaschine", "mouseClicked()") }} Methode:
```
@Override
public void mouseClicked() {
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
moleX = random(50, canvasWidth - 50);
moleY = random(50, canvasHeight - 50);
redraw();
}
}
```
??? example "Quelltext"
```Java
public class Shapes extends Zeichenmaschine {
private int moleRadius = 20;
private int moleX;
private int moleY;
public Shapes() {
super(800, 800, "Shapes");
}
@Override
public void setup() {
background.setColor(BLUE);
drawing.setFillColor(255, 223, 34);
drawing.noStroke();
moleX = random(50, canvasWidth - 50);
moleY = random(50, canvasHeight - 50);
}
@Override
public void draw() {
drawing.clear();
drawing.circle(moleX, moleY, moleRadius);
}
@Override
public void mouseClicked() {
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
moleX = random(50, canvasWidth - 50);
moleY = random(50, canvasHeight - 50);
redraw();
}
}
}
```
!!! warning ""
Der Aufruf von {{ jdm("Zeichenmaschine", "redraw()") }} zeichnet
die Zeichenfläche neu, indem die `draw()` Methode erneut aufgerufen wird.
Du solltest `draw()` niemals direkt aufrufen.
Nun springt der Kreis an eine andere Stelle, wenn er direkt mit der Maus
angeklickt wird.
<figure markdown>
![Whack-a-mole](assets/quickstart/shapes_5.3.gif){ width=400 }
</figure>
## Ein paar Details zur Zeichenmaschine
Die _Zeichenmaschine_ wurde stark von der kreativen Programmierumgebung
[Processing](https://processing.org) inspiriert. Wenn Du Processing schon
kennst, dann werden Dir einige der Konzepte der _Zeichenmaschine_ schon bekannt
vorkommen.
### Farben
Farben können auf verschiedene Weisen angegeben werden. Unser Beispiel nutzt
bisher zwei Arten:
1. Die einfachste Möglichkeit sind die _Farbkonstanten_
wie {{ jdm('Constants', 'BLUE') }} oder {{ jdm('Constants', 'RED') }}. Im
Beispiel setzen wir den Hintergrund auf die Farbe `BLUE`.
2. Farben werden häufig im RGB-Farbraum definiert. Dazu wird jeweils der Rot-,
Grün- und Blauanteil der Farbe als Wert zwischen 0 und 255 angegeben. Im
Beispiel setzen wir die Farbe der Kreise auf `255, 223, 34`, also viel Rot
und Grün und nur ein wenig Blau.
### Ebenen
Die Zeichenfläche besteht aus einzelnen {{ jdl("Layer", title="Ebenen") }}, die
auf übereinander liegen. Bis auf die unterste Ebene sind die Ebenen zunächst
durchsichtig, wodurch die Zeichnungen unterer Ebenen durchscheinen.
<figure markdown>
![Whack-a-mole](assets/quickstart/Layers.png){ width=600 }
</figure>
Eine _Zeichenmaschine_ besitzt zu Beginn drei Ebenen:
1. Die unterste Ebene ist ein {{ jdc("layers.ColorLayer") }}, die nur aus einer
Farbe (oder einem Farbverlauf) besteht und keine durchsichtigen Bereiche
besitzt. Im Beispiel setzen wir diese Ebene auf die Farbe `BLUE`.
2. Die nächste Ebene ist ein {{ jdc("layers.DrawingLayer") }}, auf die wir
unsere Formen zeichnen können. Die Ebene ist zunächst komplett durchsichtig.
3. Die oberste Ebene ist ein {{ jdc("layers.ShapesLayer") }}, die zur
Darstellung von Form-Objekten der Klasse {{ jdc("shapes.Shape") }} genutzt
werden kann.
Du kannst einer Zeichenfläche aber auch beliebige neue oder selbst programmierte
Ebenen hinzufügen.
### Ablauf
Die _Zeichenmaschine_ ruft nach dem Start die Methoden in einem festen Ablauf
auf.
<figure markdown>
![Whack-a-mole](assets/quickstart/AblaufMoleStatic.png){ width=500 }
</figure>
Erstellst Du eine _Zeichenmaschine_ (beziehungsweise ein Objekt einer
Unterklasse), dann wird zuerst die {{ jdm('Zeichenmaschine', 'setup()') }}
Methode ausgeführt. Danach folgt einmalig die
{{ jdm('Zeichenmaschine', 'draw()') }} Methode und dann endet das Hauptprogramm.
Die Eingaben der Maus werden in einem parallelen Ablauf (einem _Thread_)
abgefangen und daraufhin die {{ jdm('Zeichenmaschine', 'mouseClicked()') }}
Methode aufgerufen. In unserem Programm prüfen wir, ob mit der Maus
innerhalb des Kreises geklickt wurde und rufen dann
{{ jdm('Zeichenmaschine', 'redraw()') }} auf, woraufhin ein weiteres Mal
`draw()` ausgeführt wird.
In _Processing_ wird dies der "statische" Modus genannt, weil das Programm nur
einmal abläuft und dann stoppt. "Statisch" trifft es nicht ganz, da das Programm
ja zum Beispiel durch Mauseingaben auch verändert werden kann. Wichtig ist aber,
dass mit `redraw()` die Zeichenfläche manuell neu gezeichnet werden muss, damit
sich der Inhalt ändert.
## Dynamische Programme
Wir wollen unser kleines Spiel dynamischer machen, indem die Kreise nur drei
Sekunden angezeigt werden und dann von selbst an einen neuen Ort springen.
Schafft man es, den Kreis in dieser Zeit anzuklicken, bekommt man einen Punkt.
Als zusätzliche Herausforderung lassen wir jeden Kreis in den drei Sekunden
immer kleiner werden.
### Die Update-Draw-Schleifen
![Whack-a-mole](assets/quickstart/AblaufMoleActive.png){ width=200 align=right }
Bisher hat die _Zeichenmaschine_ einmalig `draw()` aufgerufen und dann nur noch
auf Benutzereingaben mit der Maus reagiert. Nun wollen wir das gezeichnete Bild
aber laufend anpassen. Der Kreis soll schrumpfen und nach 3 Sekunden
verschwinden.
Dazu ergänzen wir ein {{ jdl('Zeichenmaschine', 'update(double)', c=False) }}
Methode in unserem Programm. Nun schaltet die _Zeichenmaschine_ in einen
dynamischen Modus und startet die _Update-Draw-Schleife_. Das beduetet, nach
Aufruf von `setup()` wird fortlaufend immer wieder zuerst `update()` und dann
`draw()` aufgerufen.
Jeder Aufruf der `draw()` Methode zeichnet nun die Zeichenfläche neu. Jedes
Bild, das gezeichnet wird (auch, wenn es genauso aussieht, wie das davor), nennt
man ein _Frame_. Von Videospielen kennst Du vielleicht schon den Begriff "
_Frames per second_" (Fps, dt. Bilder pro Sekunde). Er bedeutet, wie oft das
Spiel neue Frames zeichnet, oder in der _Zeichenmaschine_, wie oft `draw()` pro
Sekunde aufgerufen wird. Normalerweise passiert dies genau 60-mal pro Sekunde.
### Lebenszeit eines Kreises
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
Bruchteile von Skeunden abziehen wollen, wählen wir als Datentyp `double`:
```Java
private double moleTime=3.0;
```
Der Parameter `delta`, der `update()` Methode ist der Zeitraum in Sekunden, seit
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
ist und in dem Fall den Kreis auf eine neue Position springen lassen.
Die Größe des Kreises passen wir so an, dass der Anteil der vergangenen Zeit die
Größe des Kreises bestimmt:
```Java
drawing.circle(moleX,moleY,moleRadius*(moleTime/3.0));
```
??? example "Quelltext"
```Java
import schule.ngb.zm.Zeichenmaschine;
public class Shapes extends Zeichenmaschine {
private int moleRadius = 20;
private int moleX;
private int moleY;
private double moleTime;
public Shapes() {
super(800, 800, "Shapes");
}
@Override
public void setup() {
background.setColor(BLUE);
drawing.setFillColor(255, 223, 34);
drawing.noStroke();
randomizeMole();
}
private void randomizeMole() {
moleX = random(moleRadius*2, canvasWidth - moleRadius*2);
moleY = random(moleRadius*2, canvasHeight - moleRadius*2);
moleTime = 3.0;
}
@Override
public void update( double delta ) {
moleTime -= delta;
if( moleTime <= 0 ) {
randomizeMole();
}
}
@Override
public void draw() {
drawing.clear();
drawing.circle(moleX, moleY, moleRadius * (moleTime / 3.0));
}
@Override
public void mouseClicked() {
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
randomizeMole();
redraw();
}
}
public static void main( String[] args ) {
new Shapes();
}
}
```
<figure markdown>
![Whack-a-mole](assets/quickstart/shapes_6.1.gif){ width=400 }
</figure>
!!! tip ""
Der Maulwurf muss mittlerweile an drei verschiedenen Stellen im Programm
auf eine zufällige Position gesetzt werden (am Anfang, wenn er angeklickt
wurde und wenn die drei Sekunden abgelaufen sind). Daher wurde das Versetzen
in eine eigene Methode `randomizeMole()` ausgelagert.
### Punktezähler
Zum Schluss wollen wir noch bei jedem Treffer mit der Maus die Punkte Zählen und
als Text auf die Zeichenfläche schreiben.
Dazu ergänzen wir eine weitere Objektvariable `score`, die in `mouseClicked()`
erhöht wird, falls der Kreis getroffen wurde.
Um den Punktestand anzuzeigen ergänzen wir in `draw()` einen Aufruf von
{{ jdl('layers.DrawingLayer', 'text(java.lang.String,double,double,schule.ngb.zm.Options.Direction)') }}
mit dem Inhalt von `score` und den Koordinaten, an denen der Text gezeigt
werden soll.
Die Zeichenebene zeichnet im Moment alle Formen und Text ausgehend vom Zentrum
der Form. Damit der Text 10 Pixel vom Rand entfernt links oben angezeigt wird,
können wir der Text Methode (und allen anderen, die etwas zeichnen) eine {{
jdl('Options.Direction', title='Richtung') }} übergeben, die festlegt, von
welchem Ausgangspunkt (oder _Ankerpunkt_) die Form gezeichnet werden soll.
```Java
drawing.setFillColor(BLACK);
drawing.text("Punkte: "+score,10,10,NORTHWEST);
```
<figure markdown>
![Whack-a-mole](assets/quickstart/shapes_6.2.png){ width=400 }
</figure>
??? example "Quelltext"
```Java
import schule.ngb.zm.Zeichenmaschine;
public class Shapes extends Zeichenmaschine {
private int moleRadius = 20;
private int moleX;
private int moleY;
private double moleTime;
private int score = 0;
public Shapes() {
super(800, 800, "Shapes");
}
@Override
public void setup() {
background.setColor(BLUE);
drawing.noStroke();
drawing.setFontSize(24);
randomizeMole();
}
private void randomizeMole() {
moleX = random(moleRadius*2, canvasWidth - moleRadius*2);
moleY = random(moleRadius*2, canvasHeight - moleRadius*2);
moleTime = 3.0;
}
@Override
public void update( double delta ) {
moleTime -= delta;
if( moleTime <= 0 ) {
randomizeMole();
}
}
@Override
public void draw() {
drawing.clear();
drawing.setFillColor(255, 223, 34);
drawing.circle(moleX, moleY, moleRadius * (moleTime / 3.0));
drawing.setFillColor(BLACK);
drawing.text("Punkte: " + score, 10, 10, NORTHWEST);
}
@Override
public void mouseClicked() {
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
score += 1;
randomizeMole();
redraw();
}
}
public static void main( String[] args ) {
new Shapes();
}
}
```
## Wie es weitergehen kann
In diesem Schnellstart-Tutorial hast Du die Grundlagen der _Zeichenmaschine_
gelernt. Um weiterzumachen, kannst Du versuchen, das Whack-a-mole Spiel um diese
Funktionen zu erweitern:
- Mehrere "Maulwürfe" gleichzeitig.
- Unterschiedliche Zeiten pro Maulwurf.
Wenn Du mehr über die Möglichkeiten lernen möchtest, die Dir die
_Zeichenmaschine_ bereitstellt, kannst Du Dir die weiteren Tutorials in dieser
Dokumentation ansehen. Ein guter Startpunkt ist das
[Aquarium](tutorials/aquarium/aquarium1.md).
Viele verschiedene Beispiele, ohne detailliertes Tutorial, findest Du in der
Kategorie Beispiele und auf GitHub im Repository
[jneug/zeichenmaschine-examples](https://github.com/jneug/zeichenmaschine-examples).

View File

@@ -0,0 +1,19 @@
# Tutorial: Aquarium
In diesem Tutorial wollen wir mithilfe der _Zeichenmaschine_ ein (bonbonbuntes)
interaktives Aquarium entwickeln. Dabei werden wir in verschiedenen Ausbaustufen
zunächst das System Modellieren und dann implementieren.
!!! info "Mein bonbonbuntes Aquarium"
Das Projekt [Mein bonbonbuntes Aquarium](http://blog.schockwellenreiter.de/2021/02/2021021201.html)
stammt ursprünglich aus dem Blog [Schockwellenreiter](http://blog.schockwellenreiter.de)
von [Jörg Kantel](http://cognitiones.kantel-chaos-team.de/cv.html).
Das Endprodukt soll folgendes umfassen:
- Darstellung eines hübschen Aquariums mit Fischen, die hin und her schwimmen.
- Zur Darstellung wollen wir wie im Original die Sprites aus dem [Fish Pack von Kenny.nl](https://www.kenney.nl/assets/fish-pack) nutzen.
- Das Aquarium soll durch passende Geräusche untermalt werden.
- Bei einem Klick in das Aquarium soll ein zufälliger Fisch erscheinen.
- Bei einem Druck auf die Leertaste soll ein Hai durch das Aquarium schwimmen und alle Fische auf seinem Weg auffressen.

View File

@@ -1,53 +0,0 @@
import schule.ngb.zm.Zeichenmaschine;
import schule.ngb.zm.util.ImageLoader;
public class Aquarium extends Zeichenmaschine {
public static final int N_FISHES = 25;
public static void main( String[] args ) {
new Aquarium();
}
private Fish[] fish;
public Aquarium() {
super(800, 600, "Aquarium");
}
@Override
public void setup() {
canvas.addLayer(1, new Background());
fish = new Fish[N_FISHES];
for( int i = 1; i <= 7; i++ ) {
ImageLoader.preloadImage("fish"+i, "tiles/fish"+i+"gs.png");
}
for( int i = 0; i < N_FISHES; i++ ) {
fish[i] = new Fish();
shapes.add(fish[i]);
}
}
@Override
public void update( double delta ) {
for( int i = 0; i < fish.length; i++ ) {
fish[i].update(delta);
}
}
@Override
public void draw() {
}
@Override
public void mouseClicked() {
for( int i = 0; i < fish.length; i++ ) {
fish[i].randomize();
}
}
}

View File

@@ -1,59 +0,0 @@
import schule.ngb.zm.Layer;
import java.awt.Graphics2D;
import java.awt.Image;
public class Background extends Layer {
private static final int TILE_SIZE = 64;
private int tile_width;
private Image[] floor, plants;
private Image water;
public Background() {
super();
}
public Background( int width, int height ) {
super(width, height);
}
@Override
public void setSize( int width, int height ) {
super.setSize(width, height);
generateBackground();
}
public void generateBackground() {
tile_width = (int)(ceil(getWidth()/(double)TILE_SIZE));
floor = new Image[tile_width];
plants = new Image[tile_width];
for ( int i = 0; i < tile_width; i++ ) {
floor[i] = loadImage("tiles/floor"+random(1,8)+".png");
if( random(1,10) < 4 ) {
plants[i] = loadImage("tiles/plant"+random(1,14)+".png");
}
}
water = loadImage("tiles/water.png");
for ( int i = 0; i < getHeight(); i += TILE_SIZE ) {
for ( int j = 0; j < getWidth(); j += TILE_SIZE ) {
drawing.drawImage(water, j, i, null);
}
}
for ( int i = 0; i < tile_width; i++ ) {
if( plants[i] != null ) {
drawing.drawImage(plants[i], i*TILE_SIZE, getHeight() - (2*TILE_SIZE) + 10,TILE_SIZE,TILE_SIZE, null);
}
drawing.drawImage(floor[i], i*TILE_SIZE, getHeight() - TILE_SIZE,TILE_SIZE,TILE_SIZE, null);
}
}
}

View File

@@ -1,72 +0,0 @@
import schule.ngb.zm.*;
import schule.ngb.zm.shapes.Picture;
import schule.ngb.zm.shapes.Shape;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
public class Fish extends Shape implements Updatable {
private int speed;
private Picture img;
public Fish() {
randomize();
}
public void randomize() {
int i = random(1, 7);
speed = random(-2, 2);
img = new Picture("fish" + i);
img.setAnchor(NORTHWEST);
img.scale(random(0.8, 1.0));
img.tint(randomNiceColor());
//img.scale(0.5);
if( speed < 0 ) {
img.flip(LEFT);
}
img.moveTo(random(10, width-img.getWidth()), random(30, height-120));
}
@Override
public boolean isActive() {
return true;
}
@Override
public void update( double delta ) {
img.move(speed, .5 * sin(tick / (speed * 10.0)));
if( img.getX() <= 0 || img.getX()+img.getWidth() >= 800 ) {
speed *= -1;
img.flip(LEFT);
}
}
@Override
public void draw( Graphics2D graphics, AffineTransform transform ) {
img.draw(graphics, transform);
}
@Override
public double getWidth() {
return img.getWidth();
}
@Override
public double getHeight() {
return img.getHeight();
}
@Override
public Shape copy() {
return img.copy();
}
@Override
public java.awt.Shape getShape() {
return img.getShape();
}
}

View File

@@ -1,55 +0,0 @@
#BlueJ package file
dependency1.from=Attractor
dependency1.to=Gravity
dependency1.type=UsesDependency
dependency2.from=Gravity
dependency2.to=Mover
dependency2.type=UsesDependency
dependency3.from=Gravity
dependency3.to=Attractor
dependency3.type=UsesDependency
editor.fx.0.height=728
editor.fx.0.width=1037
editor.fx.0.x=95
editor.fx.0.y=53
objectbench.height=94
objectbench.width=776
package.divider.horizontal=0.6
package.divider.vertical=0.8305369127516778
package.editor.height=488
package.editor.width=661
package.editor.x=374
package.editor.y=158
package.frame.height=660
package.frame.width=800
package.numDependencies=3
package.numTargets=3
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.height=60
readme.name=@README
readme.width=48
readme.x=10
readme.y=10
target1.height=70
target1.name=Mover
target1.showInterface=false
target1.type=ClassTarget
target1.width=120
target1.x=380
target1.y=220
target2.height=70
target2.name=Attractor
target2.showInterface=false
target2.type=ClassTarget
target2.width=120
target2.x=380
target2.y=350
target3.height=70
target3.name=Gravity
target3.showInterface=false
target3.type=ClassTarget
target3.width=120
target3.x=120
target3.y=120

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 904 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

View File

@@ -1,128 +0,0 @@
import schule.ngb.zm.*;
import java.util.List;
public class Boid extends Creature {
public static final double COHESION_FACTOR = .5;
public static final double SEPARATION_FACTOR = .5;
public static final double ALIGNMENT_FACTOR = .5;
public static final double FLIGHT_FACTOR = .5;
public static final double VELOCITY_LIMIT = 3.0;
public static final double FORCE_LIMIT = .1;
public static final int FLOCK_RADIUS = 100;
public static final boolean SHOW_RADIUS = false;
public static final int BOID_WIDTH = 8;
public static final int BOID_HEIGHT = 16;
private boolean highlight = false;
public Boid() {
// Generate random Boid
super(
Vector.random(BOID_WIDTH, width-BOID_WIDTH, BOID_HEIGHT, height-BOID_HEIGHT),
Vector.random(-1, 1).scale(random(VELOCITY_LIMIT))
);
senseRange = FLOCK_RADIUS;
}
public Boid( Vector pos, Vector vel ) {
super(pos, vel);
senseRange = FLOCK_RADIUS;
}
public void toggleHighlight() {
highlight = !highlight;
}
public void draw( DrawingLayer drawing ) {
drawing.setFillColor(251, 241, 195);
drawing.setStrokeColor(239, 191, 77);
drawing.setStrokeWeight(2);
drawing.pushMatrix();
drawing.translate(position.x, position.y);
drawing.rotate(90 + velocity.angle());
drawing.triangle(BOID_WIDTH / -2.0, BOID_HEIGHT / 2.0, 0, BOID_HEIGHT / -2.0, BOID_WIDTH / 2.0, BOID_HEIGHT / 2.0);
if( SHOW_RADIUS || highlight ) {
drawing.setFillColor(251, 241, 195, 33);
drawing.noStroke();
drawing.circle(0, 0, FLOCK_RADIUS);
}
drawing.popMatrix();
}
public void update( List<Creature> creatures ) {
Vector cohesion = new Vector();
Vector separation = new Vector();
Vector alignment = new Vector();
Vector flight = new Vector();
int boids = 0, predators = 0;
for( Creature c: creatures ) {
if( isInRange(c) ) {
if( Predator.class.isInstance(c) ) {
double distSq = position.distanceSq(c.getPosition());
flight.add(Vector.sub(position, c.getPosition()).div(distSq));
predators += 1;
} else {
cohesion.add(c.getPosition());
alignment.add(c.getVelocity());
double distSq = position.distanceSq(c.getPosition());
separation.add(Vector.sub(position, c.getPosition()).div(distSq));
boids += 1;
}
}
}
if( boids > 0 ) {
// Cohesion
cohesion.div(boids).sub(position);
cohesion.setLength(VELOCITY_LIMIT).sub(velocity);
cohesion.limit(FORCE_LIMIT);
cohesion.scale(COHESION_FACTOR);
// Separation
separation.div(boids);
separation.setLength(VELOCITY_LIMIT).sub(velocity);
separation.limit(FORCE_LIMIT);
separation.scale(SEPARATION_FACTOR);
// Alignment
alignment.div(boids);
alignment.setLength(VELOCITY_LIMIT).sub(velocity);
alignment.limit(FORCE_LIMIT);
alignment.scale(ALIGNMENT_FACTOR);
}
if( predators > 0 ) {
flight.div(predators);
flight.setLength(VELOCITY_LIMIT).sub(velocity);
flight.limit(FORCE_LIMIT*4.0);
flight.scale(FLIGHT_FACTOR);
}
acceleration
.scale(0.0)
.add(separation)
.add(cohesion)
.add(alignment)
.add(flight);
position.add(velocity);
limitPosition();
velocity.add(acceleration).limit(VELOCITY_LIMIT);
}
}

View File

@@ -1,78 +0,0 @@
import schule.ngb.zm.*;
import schule.ngb.zm.util.ImageLoader;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
public class Boids extends Zeichenmaschine {
public static void main( String[] args ) {
new Boids();
}
public static final boolean BORDER_WRAP = true;
public static final int N_BOIDS = 200;
public static final int N_PREDATORS = 0;
private List<Creature> creatures;
public Boids() {
super(1280, 720, "ZM: Boids");
}
@Override
public void setup() {
setFullscreen(true);
setCursor(ImageLoader.loadImage("pointer.png"), 0, 0);
creatures = new ArrayList<Creature>();
synchronized( creatures ) {
for( int i = 0; i < N_BOIDS; i++ ) {
creatures.add(new Boid());
}
for( int i = 0; i < N_PREDATORS; i++ ) {
creatures.add(new Predator());
}
}
}
@Override
public void update( double delta ) {
synchronized( creatures ) {
for( Creature c : creatures ) {
c.update(creatures);
}
}
}
@Override
public void draw() {
drawing.clear(0, 125, 182);
synchronized( creatures ) {
for( Creature c : creatures ) {
c.draw(drawing);
}
}
}
@Override
public void mouseClicked() {
synchronized( creatures ) {
creatures.add(new Predator(
Vector.mouse(), Vector.ZERO
));
}
}
@Override
public void keyPressed() {
if( keyCode == KeyEvent.VK_G ) {
setSize(800, 800);
}
}
}

View File

@@ -1,77 +0,0 @@
import schule.ngb.zm.Constants;
import schule.ngb.zm.DrawingLayer;
import schule.ngb.zm.Vector;
import java.util.List;
public abstract class Creature extends Constants {
protected Vector position, velocity, acceleration;
protected int senseRange = 100;
public Creature( Vector pos, Vector vel ) {
position = pos.copy();
velocity = vel.copy();
acceleration = new Vector();
}
public Vector getPosition() {
return position;
}
public Vector getVelocity() {
return velocity;
}
public abstract void draw( DrawingLayer drawing );
public abstract void update( List<Creature> creatures );
protected void limitPosition() {
if( position.x < 0 ) {
if( Boids.BORDER_WRAP ) {
position.x = width;
} else {
position.x = 0;
//velocity.mult(-1);
acceleration.add(Vector.scale(velocity, -2));
}
} else if( position.x > width ) {
if( Boids.BORDER_WRAP ) {
position.x = 0;
} else {
position.x = width;
//velocity.mult(-1);
acceleration.add(Vector.scale(velocity, -2));
}
}
if( position.y < 0 ) {
if( Boids.BORDER_WRAP ) {
position.y = height;
} else {
position.y = 0;
//velocity.mult(-1);
acceleration.add(Vector.scale(velocity, -2));
}
} else if( position.y > height ) {
if( Boids.BORDER_WRAP ) {
position.y = 0.0;
} else {
position.y = height;
//velocity.mult(-1);
acceleration.add(Vector.scale(velocity, -2));
}
}
}
protected boolean isInRange( Creature otherCreature ) {
if( otherCreature == this || otherCreature == null ) {
return false;
}
if( abs(position.x-otherCreature.getPosition().x) > senseRange ) {
return false;
}
return position.distanceSq(otherCreature.getPosition()) < senseRange*senseRange;
}
}

View File

@@ -1,97 +0,0 @@
import schule.ngb.zm.DrawingLayer;
import schule.ngb.zm.Vector;
import java.util.List;
public class Predator extends Creature {
public static final int SENSE_RADIUS = 180;
public static final double AVOIDANCE_FACTOR = .65;
public static final double HUNTING_FACTOR = .65;
public static final double VELOCITY_LIMIT = 5.0;
public static final double FORCE_LIMIT = .1;
public static final int PREDATOR_WIDTH = 12;
public static final int PREDATOR_HEIGHT = 26;
public static final boolean SHOW_RADIUS = true;
public Predator() {
// Generate random Predator
super(
Vector.random(PREDATOR_WIDTH, width - PREDATOR_WIDTH, PREDATOR_HEIGHT, height - PREDATOR_HEIGHT),
Vector.random(-1, 1).scale(random(VELOCITY_LIMIT))
);
senseRange = SENSE_RADIUS;
}
public Predator( Vector pos, Vector vel ) {
super(pos, vel);
senseRange = SENSE_RADIUS;
}
public void draw( DrawingLayer drawing ) {
drawing.setFillColor(152, 61, 83);
drawing.setStrokeColor(225, 33, 32);
drawing.setStrokeWeight(2);
drawing.pushMatrix();
drawing.translate(position.x, position.y);
drawing.rotate(90 + velocity.angle());
drawing.triangle(PREDATOR_WIDTH / -2.0, PREDATOR_HEIGHT / 2.0, 0, PREDATOR_HEIGHT / -2.0, PREDATOR_WIDTH / 2.0, PREDATOR_HEIGHT / 2.0);
if( SHOW_RADIUS ) {
drawing.setFillColor(152, 61, 83, 33);
drawing.noStroke();
drawing.circle(0, 0, SENSE_RADIUS);
}
drawing.popMatrix();
}
public void update( List<Creature> creatures ) {
Vector hunting = new Vector();
Vector separation = new Vector();
double boids = 0, predators = 0;
for( Creature c : creatures ) {
if( isInRange(c) ) {
if( Boid.class.isInstance(c) ) {
hunting.add(c.getPosition());
boids += 1;
} else {
double distSq = position.distanceSq(c.getPosition());
separation.add(Vector.sub(position, c.getPosition()).div(distSq));
predators += 1;
}
}
}
if( boids > 0 ) {
hunting.div(boids).sub(position);
hunting.setLength(VELOCITY_LIMIT).sub(velocity);
hunting.limit(FORCE_LIMIT);
hunting.scale(HUNTING_FACTOR);
}
if( predators > 0 ) {
separation.div(predators);
separation.setLength(VELOCITY_LIMIT).sub(velocity);
separation.limit(FORCE_LIMIT);
separation.scale(AVOIDANCE_FACTOR);
}
acceleration
.scale(0.0)
.add(hunting)
.add(separation);
position.add(velocity);
limitPosition();
velocity.add(acceleration).limit(VELOCITY_LIMIT);
}
}

View File

@@ -1,55 +0,0 @@
#BlueJ package file
dependency1.from=Attractor
dependency1.to=Gravity
dependency1.type=UsesDependency
dependency2.from=Gravity
dependency2.to=Mover
dependency2.type=UsesDependency
dependency3.from=Gravity
dependency3.to=Attractor
dependency3.type=UsesDependency
editor.fx.0.height=728
editor.fx.0.width=1037
editor.fx.0.x=95
editor.fx.0.y=53
objectbench.height=94
objectbench.width=776
package.divider.horizontal=0.6
package.divider.vertical=0.8305369127516778
package.editor.height=488
package.editor.width=661
package.editor.x=374
package.editor.y=158
package.frame.height=660
package.frame.width=800
package.numDependencies=3
package.numTargets=3
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.height=60
readme.name=@README
readme.width=48
readme.x=10
readme.y=10
target1.height=70
target1.name=Mover
target1.showInterface=false
target1.type=ClassTarget
target1.width=120
target1.x=380
target1.y=220
target2.height=70
target2.name=Attractor
target2.showInterface=false
target2.type=ClassTarget
target2.width=120
target2.x=380
target2.y=350
target3.height=70
target3.name=Gravity
target3.showInterface=false
target3.type=ClassTarget
target3.width=120
target3.x=120
target3.y=120

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,101 +0,0 @@
import schule.ngb.zm.GraphicsLayer;
import schule.ngb.zm.Options;
import schule.ngb.zm.Zeichenmaschine;
import schule.ngb.zm.shapes.Picture;
import schule.ngb.zm.shapes.Text;
import java.util.ArrayList;
public class Cookieclicker extends Zeichenmaschine {
public static void main( String[] args ) {
new Cookieclicker();
}
private int cookies = 0;
private int cookiesPerClick = 1;
private int grandmas = 0;
private int autoclicker = 12, autoclickerTimer = 0, autoClickerDelay = 600;
private Text tCookies, tShopGrandma, tShopAutoclicker;
private Picture pCookie;
private boolean cookieDown = false;
public Cookieclicker() {
super(1280, 900, "Cookieclicker");
}
@Override
public void setup() {
pCookie = new Picture(width / 2, height / 2, "assets/cookie.png");
pCookie.scale(.5);
shapes.add(pCookie);
tCookies = new Text(width - 60, 60, "" + cookies);
tCookies.setAnchor(NORTHEAST);
tCookies.setStrokeColor(255);
tCookies.setFontsize(36);
shapes.add(tCookies);
background.setColor(0);
}
@Override
public void update( double delta ) {
tCookies.setText("" + cookies);
autoclickerTimer -= (int)(delta * 1000.0);
if( autoclickerTimer <= 0 ) {
cookies += autoclicker;
autoclickerTimer += autoClickerDelay;
}
synchronized( particles ) {
ArrayList<NumberParticle> remove = new ArrayList<>();
for( NumberParticle p : particles ) {
if( p.isActive() ) {
p.update(delta);
} else {
remove.add(p);
}
}
for( NumberParticle p : remove ) {
particles.remove(p);
}
}
}
@Override
public void draw() {
}
ArrayList<NumberParticle> particles = new ArrayList<>();
@Override
public void mousePressed() {
if( pCookie.getBounds().contains(mouseX, mouseY) ) {
cookieDown = true;
cookies += cookiesPerClick;
pCookie.scale(.95);
synchronized( particles ) {
NumberParticle p = new NumberParticle(mouseX, mouseY, cookiesPerClick);
particles.add(p);
shapes.add(p);
}
}
}
@Override
public void mouseReleased() {
if( cookieDown ) {
pCookie.scale(1 / .95);
}
}
}

View File

@@ -1,33 +0,0 @@
import schule.ngb.zm.Updatable;
import schule.ngb.zm.shapes.Text;
public class NumberParticle extends Text implements Updatable {
double sinOffset, life = 0;
public NumberParticle( double x, double y, int number ) {
super(x,y,"+"+number);
sinOffset = random(0.0, PI);
life = 1.5;
setStrokeColor(255);
setFontsize(36);
}
@Override
public boolean isActive() {
return (life > 0);
}
@Override
public void update( double delta ) {
if( isActive() ) {
double deltaX = sin(sinOffset + life);
x += deltaX;
y -= 100*delta;
life -= delta;
setStrokeColor(strokeColor, (int) interpolate(0, 255, life / 1.5));
}
}
}

View File

@@ -1,111 +0,0 @@
import schule.ngb.zm.Drawable;
import schule.ngb.zm.Updatable;
import schule.ngb.zm.shapes.Text;
import java.awt.Graphics2D;
import java.util.ArrayList;
public class NumberParticleEmitter implements Updatable, Drawable {
public class NumberParticle extends Text implements Updatable {
double velX = 0, velY = 0, accX = 0, accY = 0;
double life = 0;
public NumberParticle() {
super(0, 0, "0");
// life = particleLifespan;
}
@Override
public boolean isActive() {
return (life <= 0);
}
public void activate() {
x = emitterX;
y = emitterY;
life = particleLifespan;
}
@Override
public void update( double delta ) {
if( isActive() ) {
velX += accX;
velY += accY;
x += velX;
y += velY;
life -= delta;
setFillColor(fillColor, (int) interpolate(0, 255, life / particleLifespan));
}
}
}
private int particlesPerSecond, maxParticles;
private double particleLifespan;
private ArrayList<NumberParticle> particles;
private int activeParticles = 0;
private double lastEmit = 0.0;
private double emitterX, emitterY;
public NumberParticleEmitter( double x, double y, int particlesPerSecond, double particleLifespan ) {
emitterX = x;
emitterY = y;
this.particlesPerSecond = particlesPerSecond;
this.particleLifespan = particleLifespan;
maxParticles = (int) Math.ceil(particlesPerSecond * particleLifespan);
particles = new ArrayList<>(maxParticles);
for( int i = 0; i < maxParticles; i++ ) {
particles.add(new NumberParticle());
}
}
@Override
public boolean isVisible() {
return activeParticles > 0;
}
@Override
public void draw( Graphics2D graphics ) {
for( NumberParticle p : particles ) {
if( p.isActive() ) {
p.draw(graphics);
}
}
}
@Override
public boolean isActive() {
return isVisible();
}
public void emitParticle( int number ) {
particles.add(new NumberParticle());
}
@Override
public void update( double delta ) {
//lastEmit -= delta;
for( NumberParticle p : particles ) {
if( p.isActive() ) {
p.update(delta);
} /*else if( lastEmit <= 0 ) {
p.activate();
lastEmit += 1/(double)particlesPerSecond;
}*/
}
}
}

View File

@@ -1,36 +0,0 @@
import schule.ngb.zm.GraphicsLayer;
import schule.ngb.zm.Zeichenmaschine;
public class ParticleTest extends Zeichenmaschine {
public static void main( String[] args ) {
new ParticleTest();
}
private NumberParticleEmitter emitter;
private GraphicsLayer graphics;
public ParticleTest() {
super(800,800, "Particles");
}
@Override
public void setup() {
graphics = new GraphicsLayer();
canvas.addLayer(graphics);
emitter = new NumberParticleEmitter(400, 400, 1, 10);
}
@Override
public void update( double delta ) {
emitter.update(delta);
}
@Override
public void draw() {
emitter.draw(graphics.getGraphics());
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -1,55 +0,0 @@
#BlueJ package file
dependency1.from=Attractor
dependency1.to=Gravity
dependency1.type=UsesDependency
dependency2.from=Gravity
dependency2.to=Mover
dependency2.type=UsesDependency
dependency3.from=Gravity
dependency3.to=Attractor
dependency3.type=UsesDependency
editor.fx.0.height=728
editor.fx.0.width=1037
editor.fx.0.x=95
editor.fx.0.y=53
objectbench.height=94
objectbench.width=776
package.divider.horizontal=0.6
package.divider.vertical=0.8305369127516778
package.editor.height=488
package.editor.width=661
package.editor.x=374
package.editor.y=158
package.frame.height=660
package.frame.width=800
package.numDependencies=3
package.numTargets=3
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.height=60
readme.name=@README
readme.width=48
readme.x=10
readme.y=10
target1.height=70
target1.name=Mover
target1.showInterface=false
target1.type=ClassTarget
target1.width=120
target1.x=380
target1.y=220
target2.height=70
target2.name=Attractor
target2.showInterface=false
target2.type=ClassTarget
target2.width=120
target2.x=380
target2.y=350
target3.height=70
target3.name=Gravity
target3.showInterface=false
target3.type=ClassTarget
target3.width=120
target3.x=120
target3.y=120

View File

@@ -1,51 +0,0 @@
import schule.ngb.zm.Color;
import schule.ngb.zm.DrawingLayer;
import schule.ngb.zm.Vector;
import schule.ngb.zm.Zeichenobjekt;
public class Eye extends Zeichenobjekt {
private Vector position;
private double size;
private Color color;
public Eye() {
position = Vector.random(0, width, 0, height);
size = random(10.0, 25.0);
color = null;
}
public Eye( float x, float y ) {
position = new Vector(x, y);
size = random(10.0, 25.0);
color = null;
}
public Eye( float x, float y, Color color ) {
position = new Vector(x, y);
size = random(10.0, 25.0);
this.color = color;
}
@Override
public void draw( DrawingLayer drawing ) {
Vector dir = Vector.sub(new Vector(mouseX, mouseY), position);
double len = dir.length();
drawing.setStrokeColor(0);
if( color == null ) {
drawing.setFillColor(colorHsb(354, 100.0 - limit(len, 0.0, 100.0), 100));
} else {
drawing.setFillColor(color);
}
drawing.circle(position.x, position.y, size);
Vector pupil = Vector.add(position, dir.limit(size*.4));
drawing.setFillColor(0);
drawing.circle(pupil.x, pupil.y, size*.4);
}
}

View File

@@ -1,38 +0,0 @@
import schule.ngb.zm.Zeichenmaschine;
public class Eyes extends Zeichenmaschine {
public static final int N_EYES = 30;
public static void main( String[] args ) {
new Eyes();
}
Eye[] eyes;
public Eyes() {
super(600, 600, "Eyes");
}
@Override
public void setup() {
eyes = new Eye[N_EYES];
for( int i = 0; i < eyes.length; i++ ) {
eyes[i] = new Eye();
}
}
@Override
public void update( double delta ) {
}
@Override
public void draw() {
drawing.clear(200);
for( int i = 0; i < eyes.length; i++ ) {
eyes[i].draw(drawing);
}
}
}

View File

@@ -1,55 +0,0 @@
#BlueJ package file
dependency1.from=Attractor
dependency1.to=Gravity
dependency1.type=UsesDependency
dependency2.from=Gravity
dependency2.to=Mover
dependency2.type=UsesDependency
dependency3.from=Gravity
dependency3.to=Attractor
dependency3.type=UsesDependency
editor.fx.0.height=728
editor.fx.0.width=1037
editor.fx.0.x=95
editor.fx.0.y=53
objectbench.height=94
objectbench.width=776
package.divider.horizontal=0.6
package.divider.vertical=0.8305369127516778
package.editor.height=488
package.editor.width=661
package.editor.x=374
package.editor.y=158
package.frame.height=660
package.frame.width=800
package.numDependencies=3
package.numTargets=3
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.height=60
readme.name=@README
readme.width=48
readme.x=10
readme.y=10
target1.height=70
target1.name=Mover
target1.showInterface=false
target1.type=ClassTarget
target1.width=120
target1.x=380
target1.y=220
target2.height=70
target2.name=Attractor
target2.showInterface=false
target2.type=ClassTarget
target2.width=120
target2.x=380
target2.y=350
target3.height=70
target3.name=Gravity
target3.showInterface=false
target3.type=ClassTarget
target3.width=120
target3.x=120
target3.y=120

View File

@@ -1,94 +0,0 @@
import schule.ngb.zm.Zeichenmaschine;
import java.util.LinkedList;
public class Atoms extends Zeichenmaschine {
/**
* Liste der beweglichen Objekte in der Welt.
*/
private LinkedList<Mover> movers = new LinkedList<>();
/**
* Liste der Gravitationsquellen in der Welt.
*/
private LinkedList<Attractor> attractors = new LinkedList<>();
/**
* Erstellt die {@link Mover}- und {@link Attractor}-Objekte.
*/
public void setup() {
addAttractor( width / 2, height / 2, 20 );
attractors.getFirst().setMovable(false);
attractors.getFirst().setActive(false);
attractors.getFirst().hide();
addAttractor( width*.4, height*.4, 8 );
addAttractor( width*.6, height*.6, 8 );
for( int i = 0; i < 10; i++ ) {
Mover m = new Mover(random(10, width - 10), random(10, height - 10));
movers.add(m);
shapes.add(m);
}
}
private void addAttractor( double pX, double pY, int pMass ) {
Attractor a = new Attractor((int)pX, (int)pY, pMass);
attractors.add(a);
movers.add(a);
shapes.add(a);
}
/**
* Aktualisiert die Beschleunigung der {@link Mover}-Objekte durch Anwenden
* der einwirkenden Kräfte und aktualisiert dann die Position entsprechend
* der Beschleunigung.
* <p>
* Die Position des ersten {@link Attractor} wird auf die Mausposition
* gesetzt.
*
* @param delta
*/
public void update( double delta ) {
// Erste Gravitationsquelle auf Mausposition setzen.
Attractor mouseFollow = attractors.get(0);
if( mouseFollow.isActive() ) {
mouseFollow.moveTo(mouseX, mouseY);
}
// Kräfte anwenden
for( Attractor a : attractors ) {
if( a.isActive() ) {
for( Mover m : movers ) {
if( m.isActive() ) {
a.attract(m);
}
}
}
}
// Position aktualisieren
for( Mover m : movers ) {
if( m.isActive() ) {
m.update(delta);
}
}
}
/**
* Setzt die Position und Beschleunigung aller {@link Mover}-Objekte in der
* Welt zurück.
*/
public void mouseClicked() {
for( Mover m : movers ) {
m.moveTo(random(10, width - 10), random(10, height - 10));
m.setVelocity(0, 0);
}
}
public static void main( String[] args ) {
new Atoms();
}
}

View File

@@ -1,110 +0,0 @@
import schule.ngb.zm.Vector;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
/**
* Gravitationsquelle in der Simulation.
* <p>
* Eine Gravitationsquelle zieht mit einer Anziehungskraft proportional zu
* seiner Masse alle {@link Mover}-Objekte an. Dabei kommt die Newtonsche
* Gravitationsformel zur Anwendung.
* <p>
* Ein <code>Attractor</code> ist auch ein {@link Mover} und wird von anderen
* Gravitationsquellen beeinflusst. Dieses Verhalten kann durch Setzen von
* <code>setMovable(false)</code> abgeschaltet werden.
*/
public class Attractor extends Mover {
/**
* Gravitationskonstante
* <p>
* Beeinflusst die Stärke der Anziehungskraft der {@link Attractor}en.
*/
public static final int G = 25;
/**
* Ob dieser <code>Attractor</code> auch von anderen Kräften beeinflusst wird.
*/
private boolean movable = true;
/**
* Erstellt einen <code>Attractor</code> an der angegebenen Position mit der angegebenen
* Masse.
*
* @param pX x-Koordinate des Objektes.
* @param pY y-Koordinate des Objektes.
* @param pMass Masse des Objektes.
*/
public Attractor( int pX, int pY, int pMass ) {
this(pX, pY, pMass, new Vector());
}
/**
* Erstellt einen <code>Attractor</code> an der angegebenen Position
*
* @param pX x-Koordinate des Objektes.
* @param pY y-Koordinate des Objektes.
* @param pMass Masse des Objektes.
* @param pVelocity Initialgeschwindigkeit des Objektes.
*/
public Attractor( int pX, int pY, int pMass, Vector pVelocity ) {
super(pX, pY, pVelocity);
mass = pMass;
setFillColor(randomColor());
}
/**
* Stellt ein, ob dieser <code>Attractor</code> auch von anderen Kräften
* beeinflusst wird, oder ob er starr an einer Position bleibt.
*
* @param pMovable <code>true</code> oder <code>false</code>.
*/
public void setMovable( boolean pMovable ) {
this.movable = pMovable;
}
/**
* Wendet die Anziehungskraft des <code>Attractor</code> auf einen
* <code>Mover</code> an.
*
* @param pMover Das Objekt, das angezogen wird.
*/
public void attract( Mover pMover ) {
if( pMover != this && isActive() ) {
Vector force = new Vector(this.x, this.y);
force.sub(pMover.getX(), pMover.getY());
double v = G * mass / force.lengthSq();
force.setLength(v).limit(1.0, 4 * G);
pMover.applyForce(force);
}
}
/**
* Aktualisiert die momentante Geschwindigkeit und Position des Objektes und
* setzt die Beschleunigung zurück.
*
* @param delta Zeitintervall seit dem letzten Aufruf (in Sekunden).
*/
@Override
public void update( double delta ) {
if( movable ) {
super.update(delta);
}
}
@Override
public void draw( Graphics2D graphics, AffineTransform transform ) {
double m = 2.0*mass;
AffineTransform at = graphics.getTransform();
graphics.transform(transform);
graphics.setColor(new java.awt.Color(255,193,64,66));
graphics.fillOval((int)(-.5*m), (int)(-.5*m), (int)(2*getRadius()+m), (int)(2*getRadius()+m));
graphics.setTransform(at);
super.draw(graphics, transform);
}
}

Some files were not shown because too many files have changed in this diff Show More