true, wenn das Objekt aktiv ist.
+ */
+ public boolean istAktiv();
+
+ /**
+ * Änderung des Zustandes des Objekts abhängig vom Zeitintervall
+ * delta in Sekunden.
+ * @param delta Zeitintervall seit dem letzten Aufruf (in Sekunden).
+ */
+ public void aktualisieren( double delta );
+
+}
diff --git a/src/schule/ngb/zm/Ebene.java b/src/schule/ngb/zm/Ebene.java
new file mode 100644
index 0000000..fd64ccf
--- /dev/null
+++ b/src/schule/ngb/zm/Ebene.java
@@ -0,0 +1,106 @@
+package schule.ngb.zm;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public abstract class Ebene extends Konstanten implements Zeichenbar, Aktualisierbar {
+
+ protected BufferedImage puffer;
+
+ protected Graphics2D zeichnung;
+
+ protected boolean sichtbar = true;
+
+ protected boolean aktiv = true;
+
+
+ public Ebene() {
+ this(STD_BREITE, STD_HOEHE);
+ }
+
+ public Ebene( int pBreite, int pHoehe ) {
+ zeichnungErstellen(pBreite, pHoehe);
+ }
+
+ public int getBreite() {
+ return puffer.getWidth();
+ }
+
+ public int getHoehe() {
+ return puffer.getHeight();
+ }
+
+ public void setGroesse( int pBreite, int pHoehe ) {
+ if( puffer != null ) {
+ zeichnenWiederherstellen(pBreite, pHoehe);
+ } else {
+ zeichnungErstellen(pBreite, pHoehe);
+ }
+ }
+
+ private void zeichnungErstellen( int pBreite, int pHoehe ) {
+ puffer = new BufferedImage(pBreite, pHoehe, BufferedImage.TYPE_INT_ARGB);
+ zeichnung = puffer.createGraphics();
+
+ // add antialiasing
+ RenderingHints hints = new RenderingHints(
+ RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON
+ );
+ hints.put(
+ RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY
+ );
+ zeichnung.addRenderingHints(hints);
+ }
+
+ private void zeichnenWiederherstellen( int pBreite, int pHoehe ) {
+ BufferedImage oldCanvas = puffer;
+ zeichnungErstellen(pBreite, pHoehe);
+ zeichnung.drawImage(oldCanvas, 0, 0, null);
+ }
+
+ /**
+ * Leert die Ebene und löscht alles bisher gezeichnete.
+ */
+ public abstract void leeren();
+
+ /**
+ * Zeichnet den Puffer auf die Grafik-Instanz.
+ *
+ * @param pGraphics
+ */
+ @Override
+ public void zeichnen( Graphics2D pGraphics ) {
+ if( sichtbar ) {
+ pGraphics.drawImage(puffer, 0, 0, null);
+ }
+ }
+
+ @Override
+ public boolean istSichtbar() {
+ return sichtbar;
+ }
+
+ public void verstecken() {
+ sichtbar = false;
+ }
+
+ public void zeigen() {
+ sichtbar = true;
+ }
+
+ public void umschalten() {
+ sichtbar = !sichtbar;
+ }
+
+ @Override
+ public void aktualisieren( double delta ) {
+ }
+
+ @Override
+ public boolean istAktiv() {
+ return aktiv;
+ }
+
+}
diff --git a/src/schule/ngb/zm/Farbe.java b/src/schule/ngb/zm/Farbe.java
new file mode 100644
index 0000000..2c2327f
--- /dev/null
+++ b/src/schule/ngb/zm/Farbe.java
@@ -0,0 +1,95 @@
+package schule.ngb.zm;
+
+import java.awt.*;
+
+public class Farbe extends Color {
+
+ public static final Farbe SCHWARZ = new Farbe(Color.BLACK);
+ public static final Farbe WEISS = new Farbe(Color.WHITE);
+ public static final Farbe GRAU = new Farbe(Color.GRAY);
+ public static final Farbe DUNKELGRAU = new Farbe(Color.DARK_GRAY);
+ public static final Farbe HELLGRAU = new Farbe(Color.LIGHT_GRAY);
+
+ public static final Farbe ROT = new Farbe(Color.RED);
+ public static final Farbe GRUEN = new Farbe(Color.GREEN);
+ public static final Farbe BLAU = new Farbe(Color.BLUE);
+ public static final Farbe GELB = new Farbe(Color.YELLOW);
+ public static final Farbe ORANGE = new Farbe(Color.ORANGE);
+ public static final Farbe CYAN = new Farbe(Color.CYAN);
+ public static final Farbe MAGENTA = new Farbe(Color.MAGENTA);
+ public static final Farbe PINK = new Farbe(Color.PINK);
+
+ public static final Farbe HGGRUEN = new Farbe(0, 165, 81);
+ public static final Farbe HGROT = new Farbe(151, 54, 60);
+
+
+ public Farbe( int pGrau ) {
+ super(pGrau, pGrau, pGrau, 255);
+ }
+
+ public Farbe( int pGrau, int pAlpha ) {
+ super(pGrau, pGrau, pGrau, pAlpha);
+ }
+
+
+ public Farbe( int pRot, int pGruen, int pBlau ) {
+ super(pRot, pGruen, pBlau);
+ }
+
+ public Farbe( int pRot, int pGruen, int pBlau, int pAlpha ) {
+ super(pRot, pGruen, pBlau, pAlpha);
+ }
+
+ public Farbe( Color pColor ) {
+ super(pColor.getRed(), pColor.getGreen(), pColor.getBlue(), pColor.getAlpha());
+ }
+
+ public Farbe( Color pColor, int pAlpha ) {
+ super(pColor.getRed(), pColor.getGreen(), pColor.getBlue(), pAlpha);
+ }
+
+ public static Farbe vonRGB( int pRGB ) {
+ return new Farbe(
+ (pRGB >> 16) & 255,
+ (pRGB >> 8) & 255,
+ pRGB & 255,
+ (pRGB >> 24) & 255);
+ }
+
+ public static Farbe vonHexcode( String pHexcode ) {
+ if( pHexcode.startsWith("#") ) {
+ pHexcode = pHexcode.substring(1);
+ }
+
+ int red = Integer.valueOf(pHexcode.substring(0, 2), 16);
+ int green = Integer.valueOf(pHexcode.substring(2, 4), 16);
+ int blue = Integer.valueOf(pHexcode.substring(4, 6), 16);
+
+ int alpha = 255;
+ if( pHexcode.length() == 8 ) {
+ alpha = Integer.valueOf(pHexcode.substring(6, 8), 16);
+ }
+
+ return new Farbe(red, green, blue, alpha);
+ }
+
+ public static Farbe morphen( Color pFarbe1, Color pFarbe2, double pFactor ) {
+ if( pFactor < 0.0 || pFarbe2 == null ) {
+ return new Farbe(pFarbe1);
+ }
+ if( pFactor > 1.0 || pFarbe1 == null )
+ return new Farbe(pFarbe2);
+ double pFactorInv = 1 - pFactor;
+ return new Farbe(
+ (int) (pFactorInv * pFarbe1.getRed() + pFactor * pFarbe2.getRed()),
+ (int) (pFactorInv * pFarbe1.getGreen() + pFactor * pFarbe2.getGreen()),
+ (int) (pFactorInv * pFarbe1.getBlue() + pFactor * pFarbe2.getBlue()),
+ (int) (pFactorInv * pFarbe1.getAlpha() + pFactor * pFarbe2.getAlpha())
+ );
+ }
+
+ public Farbe kopie() {
+ return new Farbe(this);
+ }
+
+}
diff --git a/src/schule/ngb/zm/Konstanten.java b/src/schule/ngb/zm/Konstanten.java
new file mode 100644
index 0000000..87b6d23
--- /dev/null
+++ b/src/schule/ngb/zm/Konstanten.java
@@ -0,0 +1,145 @@
+package schule.ngb.zm;
+
+import schule.ngb.zm.formen.Konturform;
+
+import java.awt.*;
+import java.awt.geom.Arc2D;
+
+public class Konstanten {
+
+ public static final String APP_NAME = "Zeichenmaschine";
+
+ public static final int STD_BREITE = 400;
+ public static final int STD_HOEHE = 400;
+ public static final int STD_FPS = 60;
+
+ public static Color STD_HINTERGRUND = new Color(200, 200, 200);
+
+
+ public static final int DURCHGEZOGEN = Konturform.DURCHGEZOGEN;
+ public static final int GESTRICHELT = Konturform.GESTRICHELT;
+ public static final int GEPUNKTET = Konturform.GEPUNKTET;
+
+ public static final int OFFEN = Arc2D.OPEN;
+ public static final int GESCHLOSSEN = Arc2D.CHORD;
+ public static final int KREISTEIL = Arc2D.PIE;
+
+
+ public static final byte ZENTRUM = 0;
+ public static final byte NORDEN = 1 << 0;
+ public static final byte OSTEN = 1 << 2;
+ public static final byte SUEDEN = 1 << 3;
+ public static final byte WESTEN = 1 << 4;
+
+ public static final byte NORDOSTEN = NORDEN | OSTEN;
+ public static final byte SUEDOSTEN = SUEDEN | OSTEN;
+ public static final byte NORDWESTEN = NORDEN | WESTEN;
+ public static final byte SUEDWESTEN = SUEDEN | WESTEN;
+
+ public static final byte MITTE = ZENTRUM;
+ public static final byte OBEN = NORDEN;
+ public static final byte RECHTS = OSTEN;
+ public static final byte UNTEN = SUEDEN;
+ public static final byte LINKS = WESTEN;
+
+
+ public static final Farbe SCHWARZ = Farbe.SCHWARZ;
+ public static final Farbe WEISS = Farbe.WEISS;
+ public static final Farbe ROT = Farbe.ROT;
+ public static final Farbe BLAU = Farbe.BLAU;
+ public static final Farbe GRUEN = Farbe.GRUEN;
+ public static final Farbe GELB = Farbe.GELB;
+
+ public Farbe farbe( int pGrau ) {
+ return farbe(pGrau, pGrau, pGrau, 255);
+ }
+
+ public Farbe farbe( int pGrau, int pAlpha ) {
+ return farbe(pGrau, pGrau, pGrau, pAlpha);
+ }
+
+ public Farbe farbe(int red, int green, int blue) {
+ return farbe(red, green, blue, 255);
+ }
+
+ public Farbe farbe(int red, int green, int blue, int alpha) {
+ if (red < 0 || red >= 256)
+ throw new IllegalArgumentException("red must be between 0 and 255");
+ if (green < 0 || green >= 256)
+ throw new IllegalArgumentException("green must be between 0 and 255");
+ if (blue < 0 || blue >= 256)
+ throw new IllegalArgumentException("blue must be between 0 and 255");
+ if (alpha < 0 || alpha >= 256)
+ throw new IllegalArgumentException("alpha must be between 0 and 255");
+
+ return new Farbe(red, green, blue, alpha);
+ }
+
+
+
+ public double abs( double x ) {
+ return Math.abs(x);
+ }
+
+ public double vorzeichen( double x ) {
+ return Math.signum(x);
+ }
+
+ public double runden( double x ) {
+ return Math.round(x);
+ }
+
+ public double abrunden( double x ) {
+ return Math.floor(x);
+ }
+
+ public double aufrunden( double x ) {
+ return Math.ceil(x);
+ }
+
+ public double sin( double x ) {
+ return Math.sin(x);
+ }
+
+ public double cos( double x ) {
+ return Math.cos(x);
+ }
+
+ public double tan( double x ) {
+ return Math.tan(x);
+ }
+
+ public double arcsin( double x ) {
+ return Math.asin(x);
+ }
+
+ public double arccos( double x ) {
+ return Math.acos(x);
+ }
+
+ public double arctan( double x ) {
+ return Math.atan(x);
+ }
+
+ public double beschraenken( double x, double max ) {
+ if( x > max ) {
+ return max;
+ }
+ return x;
+ }
+
+ public double beschraenken( double x, double min, double max ) {
+ if( x > max ) {
+ return max;
+ }
+ if( x < min ) {
+ return min;
+ }
+ return x;
+ }
+
+ public double morphen( double pVon, double pNach, double pFaktor ) {
+ return pVon - pFaktor*(pVon+pNach);
+ }
+
+}
diff --git a/src/schule/ngb/zm/Leinwand.java b/src/schule/ngb/zm/Leinwand.java
new file mode 100644
index 0000000..fe6040d
--- /dev/null
+++ b/src/schule/ngb/zm/Leinwand.java
@@ -0,0 +1,118 @@
+package schule.ngb.zm;
+
+import schule.ngb.zm.formen.Formenebene;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * Eine Leinwand ist die Hauptkomponente einer Zeichenmaschine. Sie besteht aus
+ * mehreren Ebenen, auf denen auf verschiedene Arten gezeichnet werden kann. Die
+ * Ebenen lassen sich beliebig übereinander anordenen, ausblenden oder wieder
+ * löschen.
+ *
+ * Jede Ebene besitzt eine Zeichenfläche, auf der ihre Zeichnung liegt. Diese
+ * zeichenflächen werden pro Frame einmal von "unten" nach "oben" auf diese
+ * Leinwand gezeichnet.
+ */
+public class Leinwand extends JComponent {
+
+
+ private LinkedList0).
+ *
+ * @param i Index der Ebene (beginnend bei 0).
+ * @return Die Ebene am Index i oder null.
+ * @throws IndexOutOfBoundsException Falls der Index nicht existiert.
+ */
+ public Ebene getEbene( int i ) {
+ if( ebenen.size() > i ) {
+ return ebenen.get(i);
+ } else {
+ throw new IndexOutOfBoundsException("Keine Ebene mit dem Index " + i + " vorhanden (maximum: " + (ebenen.size() - 1) + ").");
+ }
+ }
+
+ /**
+ * Holt die erste Ebene des angegebenen Typs aus der Liste der Ebenen.
+ * Existiert keine solche Ebene, wird null zurückgegeben.
+ *
+ * @param pClazz Typ der Ebene.
+ * @param true, wenn das Objekt sichtbar ist.
+ */
+ public boolean istSichtbar();
+
+ /**
+ * Wird aufgerufen, um das Objekt auf die Zeichenfläche graphics
+ * zu zeichnen.
+ *
+ * Das Objekt muss dafür Sorge tragen, dass der Zustand der Zeichenfläche
+ * (Transformationsmatrix, Farbe, ...) erhalten bleibt. Das Objekt sollte
+ * also etwaige Änderungen am Ende des Aufrufs wieder rückgängig machen.
+ *
+ * @param graphics Die Zeichenfläche.
+ */
+ public void zeichnen( Graphics2D graphics );
+
+}
diff --git a/src/schule/ngb/zm/Zeichenebene.java b/src/schule/ngb/zm/Zeichenebene.java
new file mode 100644
index 0000000..b6000c7
--- /dev/null
+++ b/src/schule/ngb/zm/Zeichenebene.java
@@ -0,0 +1,417 @@
+package schule.ngb.zm;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.util.Stack;
+
+public class Zeichenebene extends Ebene {
+
+ private int default_anchor = ZENTRUM;
+
+ protected Color strokeColor;
+
+ protected Color fillColor;
+
+ protected double strokeWeight;
+
+ protected int konturArt = DURCHGEZOGEN;
+
+ public Color getColor() {
+ return fillColor;
+ }
+
+ public void noFill() {
+ fillColor = null;
+ }
+
+ public void setColor(int gray) {
+ setColor(gray, gray, gray, 255);
+ }
+
+ public void setColor(int gray, int alpha) {
+ setColor(gray, gray, gray, alpha);
+ }
+
+ public void setColor(int red, int green, int blue) {
+ setColor(red, green, blue, 255);
+ }
+
+ public void setColor(int red, int green, int blue, int alpha ) {
+ if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255");
+ if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255");
+ if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255");
+ if (alpha < 0 || alpha >= 256) throw new IllegalArgumentException("alpha must be between 0 and 255");
+
+ setColor(new Color(red, green, blue, alpha));
+ }
+
+ public void setColor(Color pColor) {
+ fillColor = pColor;
+ zeichnung.setColor(pColor);
+ }
+
+ public Color getStrokeColor() {
+ return strokeColor;
+ }
+
+ public void noStroke() {
+ strokeColor = null;
+ }
+
+ public void setStrokeColor(int gray) {
+ setStrokeColor(gray, gray, gray, 255);
+ }
+
+ public void setStrokeColor(int gray, int alpha) {
+ setStrokeColor(gray, gray, gray, alpha);
+ }
+
+ public void setStrokeColor(int red, int green, int blue) {
+ if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255");
+ if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255");
+ if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255");
+ setStrokeColor(red, green, blue, 255);
+ }
+
+ public void setStrokeColor(int red, int green, int blue, int alpha ) {
+ if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255");
+ if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255");
+ if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255");
+ if (alpha < 0 || alpha >= 256) throw new IllegalArgumentException("alpha must be between 0 and 255");
+
+ setStrokeColor(new Color(red, green, blue, alpha));
+ }
+
+ public void setStrokeColor(Color pColor) {
+ strokeColor = pColor;
+ zeichnung.setColor(pColor);
+ }
+
+ public void setStrokeWeight( double pWeight ) {
+ strokeWeight = pWeight;
+ zeichnung.setStroke(createStroke());
+ }
+
+ protected Stroke createStroke() {
+ switch(konturArt) {
+ case GEPUNKTET:
+ return new BasicStroke(
+ (float) strokeWeight,
+ BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND,
+ 10.0f, new float[]{1.0f, 5.0f}, 0.0f);
+ case GESTRICHELT:
+ return new BasicStroke(
+ (float) strokeWeight,
+ BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND,
+ 10.0f, new float[]{5.0f}, 0.0f);
+ default:
+ return new BasicStroke(
+ (float) strokeWeight,
+ BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND);
+ }
+ }
+
+ public int getKonturArt() {
+ return konturArt;
+ }
+
+ public void setKonturArt(int konturArt) {
+ this.konturArt = konturArt;
+ }
+
+ private Stack