mirror of
https://github.com/jneug/zeichenmaschine.git
synced 2026-04-14 06:33:34 +02:00
Refactorings und Dokumentation
This commit is contained in:
@@ -2,6 +2,7 @@ package schule.ngb.zm;
|
||||
|
||||
import schule.ngb.zm.util.ImageLoader;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
@@ -71,6 +72,11 @@ public class Constants {
|
||||
public static final Color BROWN = Color.BROWN;
|
||||
|
||||
public static final Color STD_BACKGROUND = new Color(200, 200, 200);
|
||||
|
||||
public static final int NOBUTTON = MouseEvent.NOBUTTON;
|
||||
public static final int BUTTON1 = MouseEvent.BUTTON1;
|
||||
public static final int BUTTON2 = MouseEvent.BUTTON2;
|
||||
public static final int BUTTON3 = MouseEvent.BUTTON3;
|
||||
//@formatter:on
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ public final class Options {
|
||||
INITIALIZING,
|
||||
INITIALIZED,
|
||||
RUNNING,
|
||||
AUSED,
|
||||
UPDATING,
|
||||
DRAWING,
|
||||
PAUSED,
|
||||
STOPPED,
|
||||
TERMINATED
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ package schule.ngb.zm;
|
||||
|
||||
import schule.ngb.zm.shapes.ShapesLayer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.image.BufferStrategy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
@@ -12,7 +15,7 @@ import java.util.LinkedList;
|
||||
* mehreren Ebenen, auf denen auf verschiedene Arten gezeichnet werden kann. Die
|
||||
* Ebenen lassen sich beliebig übereinander aNORTHen, ausblenden oder wieder
|
||||
* löschen.
|
||||
*
|
||||
* <p>
|
||||
* Jede Ebene besitzt eine Zeichenfläche, auf der ihre Zeichnung liegt. Diese
|
||||
* zeichenflächen werden pro Frame einmal von "DOWN" nach "UP" auf diese
|
||||
* Leinwand gezeichnet.
|
||||
@@ -40,7 +43,7 @@ public class Zeichenleinwand extends Canvas {
|
||||
* Ändert die Größe der Zeichenleinwand auf die angegebene Größe in Pixeln.
|
||||
* Eine Größenänderung hat auch eine Größenänderung aller Ebenen zur Folge.
|
||||
*
|
||||
* @param width Neue Width der Leinwand in Pixeln.
|
||||
* @param width Neue Width der Leinwand in Pixeln.
|
||||
* @param height Neue Höhe der Leinwand in Pixeln.
|
||||
*/
|
||||
@Override
|
||||
@@ -59,6 +62,7 @@ public class Zeichenleinwand extends Canvas {
|
||||
/**
|
||||
* Fügt der Zeichenleinwand eine Ebene hinzu, die oberhalb aller bisherigen
|
||||
* Ebenen eingefügt wird.
|
||||
*
|
||||
* @param layer Die neue Ebene.
|
||||
*/
|
||||
public void addLayer( Layer layer ) {
|
||||
@@ -72,7 +76,8 @@ public class Zeichenleinwand extends Canvas {
|
||||
|
||||
/**
|
||||
* Fügt der Zeichenleinwand eine Ebene an einer bestimmten Stelle hinzu.
|
||||
* @param i Index der Ebene, beginnend mit <code>0</code>.
|
||||
*
|
||||
* @param i Index der Ebene, beginnend mit <code>0</code>.
|
||||
* @param layer Die neue Ebene.
|
||||
*/
|
||||
public void addLayer( int i, Layer layer ) {
|
||||
@@ -84,8 +89,18 @@ public class Zeichenleinwand extends Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Anzahl der {@link Layer Ebenen} in dieser Leinwand zurück.
|
||||
*
|
||||
* @return Die Anzahl der Ebenen.
|
||||
*/
|
||||
public int getLayerCount() {
|
||||
return layers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Liste der bisher hinzugefügten Ebenen zurück.
|
||||
*
|
||||
* @return Liste der Ebenen.
|
||||
*/
|
||||
public java.util.List<Layer> getLayers() {
|
||||
@@ -193,7 +208,7 @@ public class Zeichenleinwand extends Canvas {
|
||||
} while( strategy.contentsRestored() );
|
||||
|
||||
// Display the buffer
|
||||
if (!strategy.contentsLost()) {
|
||||
if( !strategy.contentsLost() ) {
|
||||
strategy.show();
|
||||
|
||||
Toolkit.getDefaultToolkit().sync();
|
||||
@@ -204,4 +219,5 @@ public class Zeichenleinwand extends Canvas {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ import java.io.IOException;
|
||||
*/
|
||||
public class Zeichenmaschine extends Constants implements MouseInputListener, KeyListener {
|
||||
|
||||
/**
|
||||
* Gibt an, ob die Zeichenmaschine aus BlueJ heraus gestartet wurde.
|
||||
*/
|
||||
public static boolean IN_BLUEJ;
|
||||
|
||||
static {
|
||||
@@ -30,114 +33,240 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
|
||||
/*
|
||||
* Attributes to be accessed by subclasses.
|
||||
* Objektvariablen, die von Unterklassen benutzt werden können.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Die Leinwand, auf die alles gezeichnet wird. Die Leinwand enthält
|
||||
* {@link Layer Ebenen}, die einzelne Zeichnungen enthalten. Die Inhalte
|
||||
* aller Ebenen werden einmal pro Frame auf die Hauptleinwand übertragen.
|
||||
*/
|
||||
protected Zeichenleinwand canvas;
|
||||
|
||||
/**
|
||||
* Ebene mit der Hintergrundfarbe.
|
||||
*/
|
||||
protected ColorLayer background;
|
||||
|
||||
/**
|
||||
* Zeichenebene
|
||||
*/
|
||||
protected DrawingLayer drawing;
|
||||
|
||||
/**
|
||||
* Formenebene
|
||||
*/
|
||||
protected ShapesLayer shapes;
|
||||
|
||||
/**
|
||||
* Anzahl der Ticks (Frames), die das Programm bisher läuft.
|
||||
*/
|
||||
protected int tick = 0;
|
||||
|
||||
/**
|
||||
* Die Zeit in Millisekunden, die das Programm seit seinem Start läuft.
|
||||
*/
|
||||
protected long runtime = 0L;
|
||||
|
||||
/**
|
||||
* Der Zeitunterschied zum letzten Frame in Sekunden.
|
||||
*/
|
||||
protected double delta = 0.0;
|
||||
|
||||
protected double mouseX = 0.0, mouseY = 0.0, pmouseX = 0.0, pmouseY = 0.0;
|
||||
/**
|
||||
* Die aktuelle {@code x}-Koordinate der Maus.
|
||||
*/
|
||||
protected double mouseX = 0.0;
|
||||
|
||||
/**
|
||||
* Die aktuelle {@code y}-Koordinate der Maus.
|
||||
*/
|
||||
protected double mouseY = 0.0;
|
||||
|
||||
/**
|
||||
* Die letzte {@code x}-Koordinate der Maus (wird einmal pro Frame
|
||||
* aktualisiert).
|
||||
*/
|
||||
protected double pmouseX = 0.0;
|
||||
|
||||
/**
|
||||
* Die letzte {@code y}-Koordinate der Maus (wird einmal pro Frame
|
||||
* aktualisiert).
|
||||
*/
|
||||
protected double pmouseY = 0.0;
|
||||
|
||||
/**
|
||||
* Gibt an, ob ein Mausknopf derzeit gedrückt ist.
|
||||
*/
|
||||
protected boolean mousePressed = false;
|
||||
|
||||
protected int mouseButton = 0;
|
||||
/**
|
||||
* Der aktuell gedrückte Mausknopf. Die Mausknöpfe werden durch die Konstanten
|
||||
* {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2} und {@link #BUTTON3}
|
||||
* angegeben. (Sie stimmen mit den Konstanten in {@link MouseEvent} überein.
|
||||
*
|
||||
* @see MouseEvent
|
||||
*/
|
||||
protected int mouseButton = NOBUTTON;
|
||||
|
||||
protected char key = ' ';
|
||||
|
||||
protected int keyCode = 0;
|
||||
/**
|
||||
* Das zuletzt ausgelöste {@code MouseEvent}.
|
||||
*/
|
||||
protected MouseEvent mouseEvent;
|
||||
|
||||
/**
|
||||
* Gibt an, ob derzeit eine Taste gedrückt ist.
|
||||
*/
|
||||
protected boolean keyPressed = false;
|
||||
|
||||
protected int width, height;
|
||||
/**
|
||||
* Das Zeichen der zuletzt gedrückten Taste.
|
||||
*/
|
||||
protected char key = ' ';
|
||||
|
||||
protected int screenWidth, screenHeight;
|
||||
/**
|
||||
* Der Tastencode der zuletzt gedrückten Taste. Die Keycodes können in der
|
||||
* Klasse {@link KeyEvent} nachgesehen werden. (Zum Beispiel
|
||||
* {@link KeyEvent#VK_ENTER}.)
|
||||
*/
|
||||
protected int keyCode = 0;
|
||||
|
||||
/**
|
||||
* Das zuletzt ausgelöste {@link KeyEvent}.
|
||||
*/
|
||||
protected KeyEvent keyEvent;
|
||||
|
||||
/**
|
||||
* Die Höhe der Zeichenleinwand.
|
||||
*/
|
||||
protected int width;
|
||||
|
||||
/**
|
||||
* Die Breite der Zeichenleinwand.
|
||||
*/
|
||||
protected int height;
|
||||
|
||||
/**
|
||||
* Die Breite des Bildschirms, auf dem das Zeichenfenster geöffnet wurde.
|
||||
* <p>
|
||||
* Beachte, dass sich die Breite nicht anpasst, wenn das Zeichenfenster auf
|
||||
* einen anderen Bildschirm verschoben wird.
|
||||
*/
|
||||
protected int screenWidth;
|
||||
|
||||
/**
|
||||
* Die Höhe des Bildschirms, auf dem das Zeichenfenster geöffnet wurde.
|
||||
* <p>
|
||||
* Beachte, dass sich die Höhe nicht anpasst, wenn das Zeichenfenster auf
|
||||
* einen anderen Bildschirm verschoben wird.
|
||||
*/
|
||||
protected int screenHeight;
|
||||
|
||||
|
||||
/*
|
||||
* Interne Attribute zur Steuerung der Zeichenmaschine.
|
||||
*/
|
||||
|
||||
//@formatter:off
|
||||
// Das Zeichenfenster der Zeichenmaschine
|
||||
private JFrame frame;
|
||||
|
||||
// Die Graphics-Objekte für das aktuelle Fenster.
|
||||
private GraphicsEnvironment environment;
|
||||
|
||||
private GraphicsDevice displayDevice;
|
||||
|
||||
private boolean running = false, isDrawing = false, isUpdating = false;
|
||||
// Aktueller Zustand der Zeichenmaschine.
|
||||
private Options.AppState state = Options.AppState.INITIALIZING;
|
||||
private boolean running = false;
|
||||
|
||||
// Aktuelle Frames pro Sekunde der Zeichenmaschine.
|
||||
private int framesPerSecond;
|
||||
|
||||
// Hauptthread der Zeichenmaschine.
|
||||
private Thread mainThread;
|
||||
|
||||
private boolean quitAfterTeardown = false, initialized = false;
|
||||
// Gibt an, ob nach Ende des Hauptthreads das Programm beendet werden soll,
|
||||
// oder das Zeichenfenster weiter geöffnet bleibt.
|
||||
private boolean quitAfterTeardown = false;
|
||||
//@formatter:on
|
||||
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Zeichenmaschine.
|
||||
*/
|
||||
public Zeichenmaschine() {
|
||||
this(APP_NAME + " " + APP_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Zeichenmaschine mit dem angegebene Titel.
|
||||
*
|
||||
* @param title Der Titel, der oben im Fenster steht.
|
||||
*/
|
||||
public Zeichenmaschine( String title ) {
|
||||
this(STD_WIDTH, STD_HEIGHT, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine neue zeichenmaschine mit einer Leinwand der angebenen
|
||||
* Größe und dem angegebenen Titel.
|
||||
*
|
||||
* @param width Breite der {@link Zeichenleinwand Zeichenleinwand}.
|
||||
* @param height Höhe der {@link Zeichenleinwand Zeichenleinwand}.
|
||||
* @param title Der Titel, der oben im Fenster steht.
|
||||
*/
|
||||
public Zeichenmaschine( int width, int height, String title ) {
|
||||
// Setzen des "Look&Feel"
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch( Exception e ) {
|
||||
System.err.println("Error setting the look and feel: " + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
// Looking for the screen currently holding the mouse pointer
|
||||
// that will be used as the screen device for this Zeichenmaschine
|
||||
// Wir suchen den Bildschirm, der derzeit den Mauszeiger enthält, um
|
||||
// das Zeichenfenster dort zu zentrieren.
|
||||
java.awt.Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
|
||||
environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsDevice[] devices = environment.getScreenDevices();
|
||||
for( GraphicsDevice gd: devices ) {
|
||||
for( GraphicsDevice gd : devices ) {
|
||||
if( gd.getDefaultConfiguration().getBounds().contains(mouseLoc) ) {
|
||||
displayDevice = gd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Keinen passenden Bildschirm gefunden. Wir nutzen den Standard.
|
||||
if( displayDevice == null ) {
|
||||
displayDevice = environment.getDefaultScreenDevice();
|
||||
}
|
||||
|
||||
|
||||
// Wir kennen nun den Bildschirm und können die Breite / Höhe abrufen.
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
java.awt.Rectangle displayBounds = displayDevice.getDefaultConfiguration().getBounds();
|
||||
this.screenWidth = (int)displayBounds.getWidth();
|
||||
this.screenHeight = (int)displayBounds.getHeight();
|
||||
|
||||
this.screenWidth = (int) displayBounds.getWidth();
|
||||
this.screenHeight = (int) displayBounds.getHeight();
|
||||
|
||||
// Erstellen des Zeichenfensters
|
||||
frame = new JFrame(displayDevice.getDefaultConfiguration());
|
||||
frame.setTitle(title);
|
||||
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
|
||||
// Erstellen der Leinwand
|
||||
canvas = new Zeichenleinwand(width, height);
|
||||
frame.add(canvas);
|
||||
|
||||
framesPerSecond = STD_FPS;
|
||||
|
||||
// Die drei Standardebenen merken, für den einfachen Zugriff aus unterklassen.
|
||||
background = getBackgroundLayer();
|
||||
drawing = getDrawingLayer();
|
||||
shapes = getShapesLayer();
|
||||
|
||||
// FPS setzen
|
||||
framesPerSecond = STD_FPS;
|
||||
|
||||
// Settings der Unterklasse aufrufen, falls das Fenster vor dem Öffnen
|
||||
// verändert werden soll.
|
||||
// TODO: When to call settings?
|
||||
settings();
|
||||
|
||||
// Listener hinzufügen, um auf Maus- und Tastatureingaben zu hören.
|
||||
canvas.addMouseListener(this);
|
||||
canvas.addMouseMotionListener(this);
|
||||
canvas.addKeyListener(this);
|
||||
@@ -153,40 +282,42 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
});
|
||||
|
||||
// Fenster zusammenbauen, auf dem Bildschirm zentrieren ...
|
||||
frame.pack();
|
||||
frame.setResizable(false);
|
||||
centerFrame();
|
||||
// ... und anzeigen!
|
||||
frame.setVisible(true);
|
||||
|
||||
// Nach dem Anzeigen kann die Pufferstrategie erstellt werden.
|
||||
canvas.allocateBuffer();
|
||||
|
||||
// Erstellen des Haupt-Zeichenthreads.
|
||||
running = true;
|
||||
mainThread = new Zeichenthread();
|
||||
mainThread.start();
|
||||
|
||||
//frame.requestFocusInWindow();
|
||||
canvas.requestFocus();
|
||||
|
||||
initialized = true;
|
||||
// Fertig mit der Initialisierung!
|
||||
state = Options.AppState.INITIALIZED;
|
||||
// Los geht's ...
|
||||
mainThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt ein neues Zeichenfesnter mit der aktuellen Konfiguration.
|
||||
*
|
||||
* @param title
|
||||
*/
|
||||
// TODO: Implement in conjunction with Zeichenfenster
|
||||
public final void createFrame( String title ) {
|
||||
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if( !frame.isVisible() ) {
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if( frame.isVisible() ) {
|
||||
frame.setVisible(false);
|
||||
}
|
||||
private final Zeichenfenster createFrame( String title ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zentriert das Zeichenfenster auf dem aktuellen Bildschirm.
|
||||
*/
|
||||
public final void centerFrame() {
|
||||
// TODO: Center on current display (not main display by default)
|
||||
// TODO: Position at current BlueJ windows if IN_BLUEJ
|
||||
@@ -195,11 +326,37 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
|
||||
java.awt.Rectangle bounds = displayDevice.getDefaultConfiguration().getBounds();
|
||||
frame.setLocation(
|
||||
(int)(bounds.x + (screenWidth-frame.getWidth())/2.0),
|
||||
(int)(bounds.y + (screenHeight-frame.getHeight())/2.0)
|
||||
(int) (bounds.x + (screenWidth - frame.getWidth()) / 2.0),
|
||||
(int) (bounds.y + (screenHeight - frame.getHeight()) / 2.0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt das Zeichenfenster an.
|
||||
*
|
||||
* @see JFrame#setVisible(boolean)
|
||||
*/
|
||||
public void show() {
|
||||
if( !frame.isVisible() ) {
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versteckt das Zeichenfenster.
|
||||
*
|
||||
* @see JFrame#setVisible(boolean)
|
||||
*/
|
||||
public void hide() {
|
||||
if( frame.isVisible() ) {
|
||||
frame.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeichnet die {@link Zeichenleinwand} neu und zeigt den aktuellen Inhalt
|
||||
* im Zeichenfenster an.
|
||||
*/
|
||||
public final void redraw() {
|
||||
canvas.render();
|
||||
//canvas.invalidate();
|
||||
@@ -208,15 +365,30 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
//show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stoppt den Zeichenthread. Nachdem der aktuelle Frame gezeichnet wurde
|
||||
* wird {@link #teardown()} aufgerufen. Das Programm wird nicht beendet und
|
||||
* das Zeichenfenster bleibt weiter angezeigt.
|
||||
*/
|
||||
public final void stop() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Beendet das Programm.
|
||||
*/
|
||||
public void quit() {
|
||||
//quit(!IN_BLUEJ);
|
||||
quit(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Beendet das Programm. Falls <var>exit</var> gleich {@code true} ist,
|
||||
* wird die komplette Virtuelle Maschine beendet.
|
||||
*
|
||||
* @param exit Ob die VM beendet werden soll.
|
||||
* @see System#exit(int)
|
||||
*/
|
||||
public void quit( boolean exit ) {
|
||||
frame.setVisible(false);
|
||||
canvas.dispose();
|
||||
@@ -227,6 +399,12 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzte die Größe der {@link Zeichenleinwand}.
|
||||
*
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public final void setSize( int width, int height ) {
|
||||
//frame.setSize(width, height);
|
||||
|
||||
@@ -235,30 +413,103 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
frame.pack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Breite der {@link Zeichenleinwand}.
|
||||
*
|
||||
* @return Die Breite der {@link Zeichenleinwand}.
|
||||
*/
|
||||
public final int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Höhe der {@link Zeichenleinwand}.
|
||||
*
|
||||
* @return Die Höhe der {@link Zeichenleinwand}.
|
||||
*/
|
||||
public final int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public final void setTitle( String pTitel ) {
|
||||
frame.setTitle(pTitel);
|
||||
/**
|
||||
* Setzt den Titel des Zeichenfensters.
|
||||
*
|
||||
* @param title Der Titel, der oben im Zeichenfenster angezeigt wird.
|
||||
*/
|
||||
public final void setTitle( String title ) {
|
||||
frame.setTitle(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Zeichenleinwand zurück.
|
||||
*
|
||||
* @return Die Zeichenleinwand.
|
||||
*/
|
||||
public final Zeichenleinwand getCanvas() {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt der {@link Zeichenleinwand} eine weitere {@link Layer Ebene}
|
||||
* hinzu.
|
||||
*
|
||||
* @param layer Die neue Ebene.
|
||||
*/
|
||||
public final void addLayer( Layer layer ) {
|
||||
canvas.addLayer(layer);
|
||||
layer.setSize(getWidth(), getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Anzahl der {@link Layer Ebenen} in der {@link Zeichenleinwand}
|
||||
* zurück.
|
||||
*
|
||||
* @return Die Anzahl der Ebenen.
|
||||
*/
|
||||
public int getLayerCount() {
|
||||
return canvas.getLayerCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die {@link Layer Ebene} am angegebenen Index zurück. Gibt es keine
|
||||
* Ebene mit diesem Index.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public final Layer getLayer( int index ) {
|
||||
return canvas.getLayer(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die erste (unterste) {@link Layer Ebene} der angegebenen Klasse
|
||||
* zurück.
|
||||
*
|
||||
* <pre>
|
||||
* DrawingLayer draw = getLayer(DrawingLayer.class);
|
||||
* </pre>
|
||||
*
|
||||
* @param layerClass
|
||||
* @param <LT>
|
||||
* @return
|
||||
*/
|
||||
public final <LT extends Layer> LT getLayer( Class<LT> layerClass ) {
|
||||
return canvas.getLayer(layerClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die {@link ColorLayer Ebene} mit der Hintergrundfarbe zurück. Gibt es
|
||||
* keine solche Ebene, so wird eine erstellt und der {@link Zeichenleinwand}
|
||||
* hinzugefügt.
|
||||
* <p>
|
||||
* In der Regel sollte dies dieselbe Ebene sein wie {@link #background}.
|
||||
*
|
||||
* @return Die Hintergrundebene.
|
||||
*/
|
||||
public final ColorLayer getBackgroundLayer() {
|
||||
ColorLayer layer = canvas.getLayer(ColorLayer.class);
|
||||
if( layer == null ) {
|
||||
@@ -268,6 +519,15 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Standard-{@link DrawingLayer Zeichenebene} zurück. Gibt es
|
||||
* keine solche Ebene, so wird eine erstellt und der {@link Zeichenleinwand}
|
||||
* hinzugefügt.
|
||||
* <p>
|
||||
* In der Regel sollte dies dieselbe Ebene sein wie {@link #drawing}.
|
||||
*
|
||||
* @return Die Zeichenebene.
|
||||
*/
|
||||
public final DrawingLayer getDrawingLayer() {
|
||||
DrawingLayer layer = canvas.getLayer(DrawingLayer.class);
|
||||
if( layer == null ) {
|
||||
@@ -277,6 +537,15 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Standard-{@link ShapesLayer Formenebene} zurück. Gibt es
|
||||
* keine solche Ebene, so wird eine erstellt und der {@link Zeichenleinwand}
|
||||
* hinzugefügt.
|
||||
* <p>
|
||||
* In der Regel sollte dies dieselbe Ebene sein wie {@link #shapes}.
|
||||
*
|
||||
* @return Die Formenebene.
|
||||
*/
|
||||
public final ShapesLayer getShapesLayer() {
|
||||
ShapesLayer layer = canvas.getLayer(ShapesLayer.class);
|
||||
if( layer == null ) {
|
||||
@@ -286,14 +555,29 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die aktuellen Frames pro Sekunde zurück.
|
||||
*
|
||||
* @return Angepeilte Frames pro Sekunde
|
||||
*/
|
||||
public final int getFramesPerSecond() {
|
||||
return framesPerSecond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt die Anzahl an Frames pro Sekunde auf einen neuen Wert.
|
||||
*
|
||||
* @param pFramesPerSecond Neue FPS.
|
||||
*/
|
||||
public final void setFramesPerSecond( int pFramesPerSecond ) {
|
||||
framesPerSecond = pFramesPerSecond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speichert den aktuellen Inhalt der {@link Zeichenleinwand} in einer
|
||||
* Bilddatei auf der Festplatte. Zur Auswahl der Zieldatei wird dem Nutzer
|
||||
* ein {@link JFileChooser} angezeigt.
|
||||
*/
|
||||
public void saveImage() {
|
||||
JFileChooser jfc = new JFileChooser();
|
||||
jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
|
||||
@@ -309,8 +593,12 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Speichert den aktuellen Inhalt der {@link Zeichenleinwand} in einer
|
||||
* Bilddatei im angegebenen Dateipfad auf der Festplatte.
|
||||
*/
|
||||
public void saveImage( String filepath ) {
|
||||
BufferedImage img = new BufferedImage(canvas.getWidth(),canvas.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
BufferedImage img = new BufferedImage(canvas.getWidth(), canvas.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
Graphics2D g = img.createGraphics();
|
||||
g.setColor(STD_BACKGROUND.getColor());
|
||||
@@ -320,13 +608,21 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
|
||||
try {
|
||||
ImageLoader.saveImage(img, new File(filepath), true);
|
||||
} catch ( IOException ex ) {
|
||||
} catch( IOException ex ) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine Momentanaufnahme des aktuellen Inhalts der
|
||||
* {@link Zeichenleinwand} und erstellt daraus eine {@link ImageLayer Bildebene}.
|
||||
* Die Ebene wird automatisch der {@link Zeichenleinwand} vor dem
|
||||
* {@link #background} hinzugefügt.
|
||||
*
|
||||
* @return Die neue Bildebene.
|
||||
*/
|
||||
public ImageLayer snapshot() {
|
||||
BufferedImage img = new BufferedImage(canvas.getWidth(),canvas.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
BufferedImage img = new BufferedImage(canvas.getWidth(), canvas.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
Graphics2D g = img.createGraphics();
|
||||
g.setColor(STD_BACKGROUND.getColor());
|
||||
@@ -352,38 +648,22 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
return imgLayer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Methoden, die von Unterklassen überschrieben werden können / sollen.
|
||||
/**
|
||||
* Pausiert die Schleife der Zeichenmaschine für die angegebene Anzahl an
|
||||
* Millisekunden.
|
||||
*
|
||||
* @param ms Schlafenszeit in Millisekunden.
|
||||
*/
|
||||
public void settings() {
|
||||
|
||||
}
|
||||
|
||||
public void setup() {
|
||||
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
|
||||
}
|
||||
|
||||
public void teardown() {
|
||||
|
||||
}
|
||||
|
||||
public void update( double delta ) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
public void delay( int ms ) {
|
||||
public final void delay( int ms ) {
|
||||
if( ms <= 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
long timer = 0L;
|
||||
if( isDrawing ) {
|
||||
// Immediately show the current drawing before waiting
|
||||
// Measure the render time and subtract from the waiting ms
|
||||
if( state == Options.AppState.DRAWING ) {
|
||||
// Falls gerade draw() ausgeführt wird, zeigen wir den aktuellen
|
||||
// Stand der Zeichnung auf der Leinwand an. Die Zeit für das
|
||||
// Rendern wird gemessen und von der Wartezeit abgezogen.
|
||||
timer = System.nanoTime();
|
||||
canvas.render();
|
||||
timer = System.nanoTime() - timer;
|
||||
@@ -402,36 +682,112 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Methoden, die von Unterklassen überschrieben werden können / sollen.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Die Settings werden einmal beim Erstellten der Zeichenmaschine aufgerufen.
|
||||
* <p>
|
||||
* {@code settings()} wird nur selten benötigt, wenn das Zeichenfenster
|
||||
*/
|
||||
public void settings() {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode, die von Unterklassen überschrieben werden sollte, um die
|
||||
* Zeichenmaschine vor dem Start zu konfigurieren. Hier können vorbereitende
|
||||
* Befehle ausgeführt werden, die die {@link Zeichenleinwand} zu
|
||||
* initialisieren, neue Objekte instanziieren und Variablen initialisieren.
|
||||
*/
|
||||
public void setup() {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code update()} wird einmal pro Frame vor {@link #draw()} aufgerufen, um
|
||||
* notwendige Aktualisierungen vorzunehmen. Im Gegensatz zu {@link #draw()}
|
||||
* bekommt {@code update()} zusätzlich {@link #delta} übergeben, um die
|
||||
* Aktualisierungen abhängig von der echten Verzögerung zwischen zwei Frames
|
||||
* zu berechnen.
|
||||
* <p>
|
||||
* {@code delta} wird in Sekunden angegeben. Um eine Form zum Beispiel
|
||||
* um {@code 50} Pixel pro Sekunde in {@code x}-Richtung zu bewegen,
|
||||
* kann so vorgegangen werden:
|
||||
* <pre>
|
||||
* shape.move(50*delta, 0.0);
|
||||
* </pre>
|
||||
*
|
||||
* @param delta
|
||||
*/
|
||||
public void update( double delta ) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code draw()} wird einmal pro Frame aufgerufen. Bei einer
|
||||
* {@link #getFramesPerSecond() Framerate} von {@code 60} also in etwa 60-Mal
|
||||
* pro Sekunde. In der {@code draw}-Methode wird der Inhalt der Ebenen
|
||||
* manipuliert und deren Inhalte gezeichnet. Am Ende des Frames werden alle
|
||||
* Ebenen auf die {@link Zeichenleinwand} übertragen.
|
||||
* <p>
|
||||
* {@code draw()} stellt die wichtigste Methode für eine Zeichenmaschine dar,
|
||||
* da hier die Zeichnung des Programms erstellt wird.
|
||||
*/
|
||||
public void draw() {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code teardown()} wird aufgerufen, sobald die Schleife des Hauptprogramms
|
||||
* beendet wurde. Dies passiert entweder nach dem ersten Durchlauf (wenn keine
|
||||
* eigene {@link #update(double)} erstellt wurde), nach dem Aufruf von
|
||||
* {@link #stop()} oder nachdem das {@link Zeichenfenster} geschlossen wurde.
|
||||
* <p>
|
||||
* In {@code teardown()} kann zum Beispiel der Abschlussbildschirm eines
|
||||
* Spiels oder der Abspann einer Animation angezeigt werden, oder mit
|
||||
* {@link #saveImage()} die erstellte Zeichnung abgespeichert werden.
|
||||
*/
|
||||
public void teardown() {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
/*
|
||||
* Mouse handling
|
||||
*/
|
||||
@Override
|
||||
public final void mouseClicked( MouseEvent e ) {
|
||||
saveMousePosition(e);
|
||||
mouseEvent = e;
|
||||
mouseClicked();
|
||||
}
|
||||
|
||||
public void mouseClicked() {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void mousePressed( MouseEvent e ) {
|
||||
saveMousePosition(e);
|
||||
mouseEvent = e;
|
||||
mousePressed = true;
|
||||
mouseButton = e.getButton();
|
||||
mousePressed();
|
||||
}
|
||||
|
||||
public void mousePressed() {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void mouseReleased( MouseEvent e ) {
|
||||
saveMousePosition(e);
|
||||
mouseEvent = e;
|
||||
mousePressed = false;
|
||||
mouseButton = NOBUTTON;
|
||||
mouseReleased();
|
||||
}
|
||||
|
||||
public void mouseReleased() {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -446,30 +802,32 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
|
||||
@Override
|
||||
public final void mouseDragged( MouseEvent e ) {
|
||||
saveMousePosition(e);
|
||||
mouseEvent = e;
|
||||
mouseDragged();
|
||||
}
|
||||
|
||||
public void mouseDragged() {
|
||||
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void mouseMoved( MouseEvent e ) {
|
||||
saveMousePosition(e);
|
||||
mouseEvent = e;
|
||||
mouseMoved();
|
||||
}
|
||||
|
||||
public void mouseMoved() {
|
||||
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
private void saveMousePosition( MouseEvent e ) {
|
||||
pmouseX = mouseX;
|
||||
pmouseY = mouseY;
|
||||
private void saveMousePosition( MouseEvent event ) {
|
||||
if( mouseEvent != null && event.getComponent() == canvas ) {
|
||||
pmouseX = mouseX;
|
||||
pmouseY = mouseY;
|
||||
|
||||
mouseX = e.getX();
|
||||
mouseY = e.getY();
|
||||
mouseX = event.getX();
|
||||
mouseY = event.getY();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMousePosition() {
|
||||
@@ -493,7 +851,7 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
|
||||
public void keyTyped() {
|
||||
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -504,7 +862,7 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
|
||||
public void keyPressed() {
|
||||
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -515,12 +873,13 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
}
|
||||
|
||||
public void keyReleased() {
|
||||
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
private final void saveKeys( KeyEvent e ) {
|
||||
key = e.getKeyChar();
|
||||
keyCode = e.getKeyCode();
|
||||
private final void saveKeys( KeyEvent event ) {
|
||||
keyEvent = event;
|
||||
key = event.getKeyChar();
|
||||
keyCode = event.getKeyCode();
|
||||
}
|
||||
|
||||
class Zeichenthread extends Thread {
|
||||
@@ -528,7 +887,7 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
@Override
|
||||
public final void run() {
|
||||
// Wait for full initialization before start
|
||||
while( !initialized ) {
|
||||
while( state != Options.AppState.INITIALIZED ) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
@@ -548,12 +907,13 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
// call setup of subclass
|
||||
setup();
|
||||
|
||||
state = Options.AppState.RUNNING;
|
||||
while( running ) {
|
||||
// delta in seconds
|
||||
delta = (System.nanoTime() - beforeTime) / 1000000000.0;
|
||||
beforeTime = System.nanoTime();
|
||||
|
||||
//saveMousePosition();
|
||||
saveMousePosition(mouseEvent);
|
||||
|
||||
handleUpdate(delta);
|
||||
handleDraw();
|
||||
@@ -588,29 +948,30 @@ public class Zeichenmaschine extends Constants implements MouseInputListener, Ke
|
||||
tick = _tick;
|
||||
runtime = _runtime;
|
||||
}
|
||||
state = Options.AppState.STOPPED;
|
||||
|
||||
teardown();
|
||||
state = Options.AppState.TERMINATED;
|
||||
|
||||
if( quitAfterTeardown ) {
|
||||
quit();
|
||||
}
|
||||
}
|
||||
|
||||
public void handleUpdate( double delta ) {
|
||||
if( isUpdating ) {
|
||||
return;
|
||||
if( state == Options.AppState.RUNNING ) {
|
||||
state = Options.AppState.UPDATING;
|
||||
update(delta);
|
||||
state = Options.AppState.RUNNING;
|
||||
}
|
||||
isUpdating = true;
|
||||
update(delta);
|
||||
isUpdating = false;
|
||||
}
|
||||
|
||||
public void handleDraw() {
|
||||
if( isDrawing ) {
|
||||
return;
|
||||
if( state == Options.AppState.RUNNING ) {
|
||||
state = Options.AppState.DRAWING;
|
||||
draw();
|
||||
state = Options.AppState.RUNNING;
|
||||
}
|
||||
isDrawing = true;
|
||||
draw();
|
||||
isDrawing = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user