aboutsummaryrefslogtreecommitdiff
path: root/Info/10/2/Baelle/BlueJ_Projekt/Zeichenfenster.java
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2024-05-05 14:36:59 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2024-05-05 14:36:59 +0200
commitaed3ad2011483d36b1f6f6b772a6f300cb967cc0 (patch)
tree7f1543b344bdd783db1d5b079dbd15b3e6a0bf34 /Info/10/2/Baelle/BlueJ_Projekt/Zeichenfenster.java
parenta91e3342f58a6476e17d9bd172f02b1c7cacf603 (diff)
downloadunterricht-aed3ad2011483d36b1f6f6b772a6f300cb967cc0.tar.gz
unterricht-aed3ad2011483d36b1f6f6b772a6f300cb967cc0.zip
add Baelle project for simple inheritance
Diffstat (limited to 'Info/10/2/Baelle/BlueJ_Projekt/Zeichenfenster.java')
-rw-r--r--Info/10/2/Baelle/BlueJ_Projekt/Zeichenfenster.java1866
1 files changed, 1866 insertions, 0 deletions
diff --git a/Info/10/2/Baelle/BlueJ_Projekt/Zeichenfenster.java b/Info/10/2/Baelle/BlueJ_Projekt/Zeichenfenster.java
new file mode 100644
index 00000000..e17192e3
--- /dev/null
+++ b/Info/10/2/Baelle/BlueJ_Projekt/Zeichenfenster.java
@@ -0,0 +1,1866 @@
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.*;
+/**
+ * Die Klasse stellt ein Fenster mit einer Malfläche zur Verfügung,
+ * auf der Objekte der Klassen Rechteck, Kreis und Dreieck sowie Turtle dargestellt
+ * werden können.
+ * Die Zeichenfläche wird beim ersten Anlegen eines Zeichenobjekts automatisch
+ * nach dem Muster Singleton angelegt.
+ *
+ * @author Albert Wiedemann
+ * @version 1.0
+ */
+class Zeichenfenster
+{
+ /** Interface für die Aktionsausführung. */
+ interface AktionsEmpfaenger
+ {
+ /** Methode wird vom Taktgeber aufgerufen. */
+ void Ausführen();
+ void Taste (char taste);
+ void SonderTaste (int taste);
+ void Geklickt (int x, int y, int anzahl);
+ }
+
+ /** Aufzählung der erzeugbaren Objektarten. */
+ static enum SymbolArt {kreis, dreieck, rechteck, turtle, figur, text;};
+
+ /** Einziges Objekt der Zeichenfläche. */
+ private static Zeichenfenster zeichenfläche = null;
+
+ /** Fenster für die Zeichenfläche. */
+ private JFrame fenster;
+ /** Die eigentliche Darstellungskomponente. */
+ private JComponent malfläche;
+ /** Stop-Knopf für den Taktgeber. */
+ private JButton stop;
+ /** Start-Knopf für den Taktgeber. */
+ private JButton start;
+ /** Einsteller für die Taktrate*/
+ private JSlider slider;
+ /** Feld aller zu zeichnenden Objekte. */
+ private ArrayList<GrafikSymbol> alleSymbole;
+ /** Feld aller zu zeichnenden Objekte. */
+ private ArrayList<AktionsEmpfaenger> aktionsEmpfänger;
+ /** Timerobjekt für die zentrale Zeitverwaltung */
+ private javax.swing.Timer timer;
+
+ /**
+ * Legt das Fenster und die Malfläche an
+ */
+ private Zeichenfenster ()
+ {
+ alleSymbole = new ArrayList<GrafikSymbol>();
+ aktionsEmpfänger = new ArrayList<AktionsEmpfaenger>();
+ fenster = new JFrame("Zeichenfenster");
+ fenster.setLocation(50, 50);
+ fenster.setSize(800, 600);
+ fenster.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); //Close-Button kann nicht versteckt oder abgestellt werden.
+
+ malfläche = new JComponent()
+ {
+ public void paint (Graphics g)
+ {
+ g.setColor(new Color (230, 230, 230));
+ g.fillRect(0, 0, getWidth() - 1, getHeight() - 1);
+ synchronized (malfläche)
+ {
+ for (GrafikSymbol s: alleSymbole)
+ {
+ if (s.sichtbar)
+ {
+ s.Zeichnen(g);
+ }
+ }
+ }
+ }
+ };
+ malfläche.setOpaque(true);
+ malfläche.addMouseListener(new MouseAdapter ()
+ {
+ /**
+ * Gibt den Ort eines Mouseclicks an die eigentliche Aktionsmethode weiter.
+ * @param e das zugrestellte Ereignis
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ malfläche.requestFocus();
+ ArrayList<AktionsEmpfaenger> empfänger = new ArrayList<AktionsEmpfaenger>(aktionsEmpfänger);
+ for (AktionsEmpfaenger em: empfänger)
+ {
+ em.Geklickt(e.getX(), e.getY(), e.getClickCount());
+ }
+ }
+ }
+ );
+ malfläche.addKeyListener(new KeyAdapter ()
+ {
+ /**
+ * Gibt die Taste an die eigentliche Aktionsmethode weiter.
+ * @param e das zugestellte Ereignis
+ */
+ public void keyPressed(KeyEvent e)
+ {
+ ArrayList<AktionsEmpfaenger> empfänger = new ArrayList<AktionsEmpfaenger>(aktionsEmpfänger);
+ if ((int) e.getKeyChar() == KeyEvent.CHAR_UNDEFINED)
+ {
+ switch (e.getKeyCode())
+ {
+ case KeyEvent.VK_ENTER:
+ for (AktionsEmpfaenger em: empfänger)
+ {
+ em.Taste((char) KeyEvent.VK_ENTER);
+ }
+ break;
+ default:
+ for (AktionsEmpfaenger em: empfänger)
+ {
+ em.SonderTaste(e.getKeyCode());
+ }
+ }
+ }
+ else
+ {
+ for (AktionsEmpfaenger em: empfänger)
+ {
+ em.Taste(e.getKeyChar());
+ }
+ }
+ }
+ }
+ );
+ malfläche.addComponentListener(new ComponentAdapter()
+ {
+ /**
+ * Setzt die Hintegrundbilder aller Turtle auf die neue Größe.
+ */
+ public void componentResized​(ComponentEvent e)
+ {
+ synchronized (malfläche)
+ {
+ for (GrafikSymbol s: alleSymbole)
+ {
+ if (s instanceof TurtleIntern)
+ {
+ ((TurtleIntern) s).NeueGrößeSetzen();
+ }
+ }
+ }
+ }
+ }
+ );
+ fenster.add(malfläche, BorderLayout.CENTER);
+ JPanel panel = new JPanel();
+ panel.setMinimumSize(new Dimension(200, 60));
+ panel.setSize(200,60);
+ panel.setVisible(true);
+ panel.setLayout(new GridLayout(1, 2));
+ JPanel panel2 = new JPanel();
+ panel2.setMinimumSize(new Dimension(100, 60));
+ panel2.setSize(100,60);
+ panel2.setVisible(true);
+ panel2.setLayout(new GridLayout(1, 1));
+ stop = new JButton();
+ start = new JButton();
+ start.setLocation(10, 10);
+ start.setSize(80, 30);
+ start.setText("Start");
+ start.setVisible(true);
+ start.addActionListener(new ActionListener ()
+ {
+ public void actionPerformed (ActionEvent evt)
+ {
+ TaktgeberStartenIntern();
+ malfläche.requestFocus();
+ }
+ }
+ );
+ panel2.add(start);
+ stop.setLocation(100, 10);
+ stop.setSize(80, 30);
+ stop.setText("Stop");
+ stop.setVisible(true);
+ stop.setEnabled(false);
+ stop.addActionListener(new ActionListener ()
+ {
+ public void actionPerformed (ActionEvent evt)
+ {
+ TaktgeberStoppenIntern();
+ malfläche.requestFocus();
+ }
+ }
+ );
+ panel2.add(stop);
+ panel.add(panel2);
+ slider = new JSlider(0, 1000, 100);
+ slider.setLocation(190, 10);
+ slider.setSize(160, 40);
+ slider.setMinimumSize(new Dimension(160, 40));
+ slider.setPreferredSize(new Dimension(160, 40));
+ slider.setMajorTickSpacing(100);
+ slider.setPaintTicks(true);
+ slider.setPaintLabels(true);
+ slider.setValue(1000);
+ slider.addChangeListener(new ChangeListener()
+ {
+ public void stateChanged​(ChangeEvent e)
+ {
+ timer.setDelay(slider.getValue());
+ malfläche.requestFocus();
+ }
+ }
+ );
+ panel.add(slider);
+
+ fenster.add(panel, BorderLayout.SOUTH);
+ fenster.setVisible(true);
+ malfläche.requestFocus();
+
+ timer = new javax.swing.Timer (1000, new ActionListener ()
+ {
+ /**
+ * Vom Timer aufgerufen.
+ * Erzeugt den nächsten Taktimpuls
+ * @param evt der Timerevent
+ */
+ public void actionPerformed (ActionEvent evt)
+ {
+ ArrayList<AktionsEmpfaenger> empfänger = new ArrayList<AktionsEmpfaenger>(aktionsEmpfänger);
+ for (AktionsEmpfaenger e: empfänger)
+ {
+ e.Ausführen();
+ }
+ }
+ }
+ );
+ }
+
+ /**
+ * Meldet die aktuelle Breite der Malfläche.
+ * @returns Breite der Malfläche
+ */
+ static int MalflächenBreiteGeben()
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ return zeichenfläche.malfläche.getWidth();
+ }
+
+ /**
+ * Meldet die aktuelle Höhe der Malfläche.
+ * @returns Höhe der Malfläche
+ */
+ static int MalflächenHöheGeben()
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ return zeichenfläche.malfläche.getHeight();
+ }
+
+ /**
+ * Trägt einen neuen Aktionsempfänger ein.
+ * @param neu der neue Aktionsempfänger
+ */
+ static void AktionsEmpfängerEintragen(AktionsEmpfaenger neu)
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ zeichenfläche.aktionsEmpfänger.add(neu);
+ }
+
+ /**
+ * Löscht einen Aktionsempfänger aus der Liste.
+ * @param alt der zu löschende Aktionsempfänger
+ */
+ static void AktionsEmpfängerEntfernen(AktionsEmpfaenger alt)
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ zeichenfläche.aktionsEmpfänger.remove(alt);
+ }
+
+ /**
+ * Erzeugt ein neues darzustelledes Symbol.
+ * Die möglichen Symbole sind im Aufzählungstyp SymbolArt beschrieben.
+ * @param art Art des zu erzeugenden Symbols.
+ * @return Referenz auf das Delegate-Objekt.
+ */
+ static GrafikSymbol SymbolErzeugen (SymbolArt art)
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ return zeichenfläche.SymbolAnlegen(art);
+ }
+
+ /**
+ * Startet den Taktgeber.
+ */
+ static void TaktgeberStarten ()
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ zeichenfläche.TaktgeberStartenIntern();
+ }
+
+ /**
+ * Stoppt den Taktgeber.
+ */
+ static void TaktgeberStoppen ()
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ zeichenfläche.TaktgeberStoppenIntern();
+ }
+
+ /**
+ * Ablaufgeschwindigkeit des Zeitgebers einstellen.
+ *
+ * @param dauer: Angabe in Millisekunden
+ */
+ static void TaktdauerSetzen (int dauer)
+ {
+ if (zeichenfläche == null)
+ {
+ zeichenfläche = new Zeichenfenster();
+ }
+ zeichenfläche.slider.setValue(dauer < 0 ? 0 : (dauer > 1000 ? 1000: dauer));
+ }
+
+ /**
+ * Erzeugt das neue Symbol tatsächlich.
+ * @param art Art des zu erzeugenden Symbols.
+ * @return Referenz auf das Delegate-Objekt.
+ */
+ private GrafikSymbol SymbolAnlegen (SymbolArt art)
+ {
+ GrafikSymbol neu = null;
+ switch (art)
+ {
+ case rechteck:
+ neu = new RechteckIntern();
+ break;
+ case kreis:
+ neu = new EllipseIntern();
+ break;
+ case dreieck:
+ neu = new DreieckIntern();
+ break;
+ case turtle:
+ neu = new TurtleIntern();
+ break;
+ case figur:
+ neu = new FigurIntern();
+ break;
+ case text:
+ neu = new TextIntern();
+ break;
+ }
+ synchronized (zeichenfläche.malfläche)
+ {
+ zeichenfläche.alleSymbole.add(neu);
+ }
+ malfläche.repaint();
+ return neu;
+ }
+
+ /**
+ * Startet den Taktgeber.
+ */
+ private void TaktgeberStartenIntern()
+ {
+ start.setEnabled(false);
+ stop.setEnabled(true);
+ timer.start();
+ }
+
+ /**
+ * Stoppt den Taktgeber.
+ */
+ private void TaktgeberStoppenIntern()
+ {
+ start.setEnabled(true);
+ stop.setEnabled(false);
+ timer.stop();
+ }
+
+ /**
+ * Oberklasse für alle verfügbaren Grafiksymbole.
+ * Alle Grafiksymbole werden über ihr umgebendes Rechteck beschrieben.
+ */
+ abstract class GrafikSymbol
+ {
+ /** x-Position der linken oberen Ecke. */
+ protected int x;
+ /** y-Position der linken oberen Ecke. */
+ protected int y;
+ /** Breite des umgebenden Rechtecks. */
+ protected int b;
+ /** Höhe des umgebenden Rechtecks. */
+ protected int h;
+ /** Farbe des Symbols. */
+ protected Color c;
+ /** Sichtbarkeit des Symbols. */
+ protected boolean sichtbar;
+ /** Drehwinkel (mathematisch positiver Drehsinn) des Symbols. */
+ protected int winkel;
+ /** Die Form des Grafiksymbols. */
+ protected Area form;
+ /** Farbe Hellgelb. */
+ protected final Color hellgelb = new Color(255,255,128);
+ /** Farbe Hellgrün. */
+ protected final Color hellgrün = new Color(128,255,128);
+ /** Farbe Orange. */
+ protected final Color orange = new Color(255,128,0);
+ /** Farbe Braun. */
+ protected final Color braun = new Color(128,64,0);
+
+ /**
+ * Der Konstruktor erzeugt ein rotes Symbol in der linken oberen Ecke des Fensters.
+ */
+ GrafikSymbol()
+ {
+ x = 10;
+ y = 10;
+ b = 100;
+ h = 100;
+ c = Color.RED;
+ sichtbar = true;
+ winkel = 0;
+ FormErzeugen();
+ }
+
+ /**
+ * Normiert den Winkel auf Werte im Bereich [0; 360[
+ * @param winkel der Eingabewinkel
+ * @return der normierte Winkel
+ */
+ int WinkelNormieren(int winkel)
+ {
+ while (winkel < 0)
+ {
+ winkel += 360;
+ }
+ return winkel % 360;
+ }
+
+ /**
+ * Setzt die Position (der linken oberen Ecke) des Objekts.
+ * @param x x-Position der linken oberen Ecke
+ * @param y y-Position der linken oberen Ecke
+ */
+ void PositionSetzen (int x, int y)
+ {
+ this.x = x;
+ this.y = y;
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Setzt die Größe des Objekts.
+ * @param breite (neue) Breite des Objekts
+ * @param höhe (neue) Höhe des Objekts
+ */
+ void GrößeSetzen (int breite, int höhe)
+ {
+ b = breite;
+ h = höhe;
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Bestimmt die RGB-Farbe für den gegeben String.
+ * @param farbe die Farbe als String
+ * @return die Farbe als RGB-Farbe
+ */
+ Color FarbeCodieren (String farbe)
+ {
+ farbe = farbe.toLowerCase();
+ switch (farbe)
+ {
+ case "weiß":
+ case "weiss":
+ return Color.WHITE;
+ case "rot":
+ return Color.RED;
+ case "grün":
+ case "gruen":
+ return Color.GREEN;
+ case "blau":
+ return Color.BLUE;
+ case "gelb":
+ return Color.YELLOW;
+ case "magenta":
+ return Color.MAGENTA;
+ case "cyan":
+ return Color.CYAN;
+ case "hellgelb":
+ return hellgelb;
+ case "hellgrün":
+ case "hellgruen":
+ return hellgrün;
+ case "orange":
+ return orange;
+ case "braun":
+ return braun;
+ case "grau":
+ return Color.GRAY;
+ case "schwarz":
+ return Color.BLACK;
+ default:
+ return Color.BLACK;
+ }
+ }
+
+ /**
+ * Setzt die Farbe des Objekts.
+ * @param farbe (neue) Farbe des Objekts
+ */
+ void FarbeSetzen (String farbe)
+ {
+ FarbeSetzen(FarbeCodieren(farbe));
+ }
+
+ /**
+ * Setzt die Farbe des Objekts.
+ * @param c (neue) Farbe des Objekts
+ */
+ void FarbeSetzen (Color c)
+ {
+ this.c = c;
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Setzt die Sichtbarkeit des Objekts.
+ * @param sichtbar (neue) Sichtbarkeit des Objekts
+ */
+ void SichtbarkeitSetzen (boolean sichtbar)
+ {
+ this.sichtbar = sichtbar;
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Setzt den Drehwinkel des Objekts.
+ * @param winkel der (neue) Drehwinkel des Objekts
+ */
+ void WinkelSetzen (int winkel)
+ {
+ this.winkel = WinkelNormieren(winkel);
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Entfernt das Objekt aus dem Zeichenfenster.
+ */
+ void Entfernen ()
+ {
+ synchronized (zeichenfläche.malfläche)
+ {
+ zeichenfläche.alleSymbole.remove(this);
+ zeichenfläche.malfläche.repaint();
+ }
+ }
+
+ /**
+ * Bringt das Objekt eine Ebene nach vorn.
+ */
+ void NachVornBringen ()
+ {
+ synchronized (zeichenfläche.malfläche)
+ {
+ int index = zeichenfläche.alleSymbole.indexOf(this);
+ if (index < zeichenfläche.alleSymbole.size() - 1)
+ {
+ zeichenfläche.alleSymbole.set(index, zeichenfläche.alleSymbole.get(index + 1));
+ zeichenfläche.alleSymbole.set(index + 1, this);
+ zeichenfläche.malfläche.repaint();
+ }
+ }
+ }
+
+ /**
+ * Bringt das Objekt in die vorderste Ebene.
+ */
+ void GanzNachVornBringen ()
+ {
+ synchronized (zeichenfläche.malfläche)
+ {
+ int index = zeichenfläche.alleSymbole.indexOf(this);
+ if (index < zeichenfläche.alleSymbole.size() - 1)
+ {
+ zeichenfläche.alleSymbole.remove(index);
+ zeichenfläche.alleSymbole.add(this);
+ zeichenfläche.malfläche.repaint();
+ }
+ }
+ }
+
+ /**
+ * Bringt das Objekt eine Ebene nach hinten.
+ */
+ void NachHintenBringen ()
+ {
+ synchronized (zeichenfläche.malfläche)
+ {
+ int index = zeichenfläche.alleSymbole.indexOf(this);
+ if (index > 0)
+ {
+ zeichenfläche.alleSymbole.set(index, zeichenfläche.alleSymbole.get(index - 1));
+ zeichenfläche.alleSymbole.set(index - 1, this);
+ zeichenfläche.malfläche.repaint();
+ }
+ }
+ }
+
+ /**
+ * Bringt das Objekt in die hinterste Ebene.
+ */
+ void GanzNachHintenBringen ()
+ {
+ synchronized (zeichenfläche.malfläche)
+ {
+ int index = zeichenfläche.alleSymbole.indexOf(this);
+ if (index > 0)
+ {
+ zeichenfläche.alleSymbole.remove(index);
+ zeichenfläche.alleSymbole.add(0, this);
+ zeichenfläche.malfläche.repaint();
+ }
+ }
+ }
+
+ /**
+ * Testet, ob der angegebene Punkt innerhalb der Figur ist.
+ * @param x x-Koordinate des zu testenden Punktes
+ * @param y y-Koordinate des zu testenden Punktes
+ * @return wahr, wenn der Punkt innerhalb der Figur ist
+ */
+ boolean IstInnerhalb (int x, int y)
+ {
+ return form.contains(x, y);
+ }
+
+ /**
+ * Testet, ob die beiden Figuren überlappen.
+ * @param wen die andere Form
+ * @return wahr, wenn die beiden Formen überlappen.
+ */
+ boolean Schneidet (Area wen)
+ {
+ Area area = new Area(form);
+ area.intersect (wen);
+ return !area.isEmpty();
+ }
+
+ /**
+ * Zeichnet das Objekt
+ * @param g das Grafikobjekt zum Zeichnen
+ */
+ void Zeichnen(Graphics g)
+ {
+ g.setColor(c);
+ ((Graphics2D) g).fill(form);
+ }
+
+ /**
+ * Berechnet den Drehwinkel gemäß den Konventionen des Graphik-Frameworks.
+ * Für Java: Winkel in Radians, positive Drehrichtng im Uhrzeiger.
+ * @param winkel: Der Winkel in Grad, mathematischer Drehsinn
+ * @return Winkel für Graphik-Framework
+ */
+ double DrehwinkelGeben (int winkel)
+ {
+ return - Math.PI * (winkel > 180 ? winkel - 360 : winkel) / 180.0;
+ }
+
+ /**
+ * Erstellt die Form des Objekts.
+ */
+ abstract void FormErzeugen();
+ }
+
+ /**
+ * Objekte dieser Klasse verwalten ein Rechteck.
+ */
+ private class RechteckIntern extends GrafikSymbol
+ {
+ /**
+ * Erstellt die Form des Rechtecks.
+ */
+ @Override void FormErzeugen()
+ {
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (winkel), this.x + b / 2, this.y + h / 2);
+ form = new Area(new Path2D.Double (new Rectangle2D.Double(this.x, this.y, b, h), a));
+ }
+ }
+
+ /**
+ * Objekte dieser Klasse verwalten eine Ellipse.
+ */
+ private class EllipseIntern extends GrafikSymbol
+ {
+ /**
+ * Erstellt die Form der Ellipse.
+ */
+ @Override void FormErzeugen()
+ {
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (winkel), this.x + b / 2, this.y + h / 2);
+ form = new Area(new Path2D.Double (new Ellipse2D.Double(this.x, this.y, b, h), a));
+ }
+ }
+
+ /**
+ * Objekte dieser Klasse verwalten ein Dreieck.
+ */
+ private class DreieckIntern extends GrafikSymbol
+ {
+ /**
+ * Erstellt die Form des Dreiecks.
+ */
+ @Override void FormErzeugen()
+ {
+ Polygon rand = new Polygon (new int [] {x + b / 2, x + b, x, x + b / 2},
+ new int [] {y, y + h, y + h, y}, 4);
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (winkel), this.x + b / 2, this.y + h / 2);
+ form = new Area(new Path2D.Double (rand, a));
+ }
+ }
+
+ /**
+ * Objekte dieser Klasse verwalten einen Text.
+ */
+ class TextIntern extends GrafikSymbol
+ {
+ /** Der aktuelle Text. */
+ private String text;
+ /** Die aktuelle Textgröße. */
+ float size;
+
+ /**
+ * Belegt text und size mit Defaultwerten.
+ */
+ TextIntern ()
+ {
+ super();
+ text = "Text";
+ size = 12;
+ c = Color.black;
+ }
+
+ /**
+ * Erstellt die Form des Textes.
+ * Dummy, legt ein leeres Area an.
+ */
+ @Override void FormErzeugen()
+ {
+ form = new Area();
+ }
+
+ /**
+ * Testet, ob der angegebene Punkt innerhalb der Figur ist.
+ * @param x x-Koordinate des zu testenden Punktes
+ * @param y y-Koordinate des zu testenden Punktes
+ * @return falsch
+ */
+ @Override boolean IstInnerhalb (int x, int y)
+ {
+ return false;
+ }
+
+ /**
+ * Setzt den aktuellen Text.
+ * @param t der neue Text
+ */
+ void TextSetzen (String t)
+ {
+ text = t;
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Setzt die Größe des Textes.
+ */
+ void TextGrößeSetzen (int größe)
+ {
+ size = größe;
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Vergrößert den Text.
+ */
+ void TextVergrößern()
+ {
+ if (size <= 10)
+ {
+ size += 1;
+ }
+ else if (size <= 40)
+ {
+ size += 2;
+ }
+ else
+ {
+ size += 4;
+ }
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Verkleinert den Text.
+ */
+ void TextVerkleinern()
+ {
+ if (size <= 10)
+ {
+ size -= 1;
+ }
+ else if (size <= 40)
+ {
+ size -= 2;
+ }
+ else
+ {
+ size -= 4;
+ }
+ if (size < 1)
+ {
+ size = 1;
+ }
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Zeichnet das Objekt als Dreieck in der gegebenen Farbe.
+ * @param g das Grafikobjekt zum Zeichnen
+ */
+ @Override void Zeichnen(Graphics g)
+ {
+ g.setColor(c);
+ Font f = g.getFont();
+ Font f2 = f.deriveFont(size);
+ g.setFont(f2);
+
+ if (winkel == 0)
+ {
+ g.drawString(text, x, y);
+ }
+ else
+ {
+ Graphics2D g2 = (Graphics2D) g;
+ AffineTransform alt = g2.getTransform();
+ //g2.rotate(DrehwinkelGeben (winkel), x + b / 2, y + h / 2);
+ //g2.rotate(DrehwinkelGeben (winkel), x + text.length() * size / 4, y + size / 2);
+ Rectangle2D bounds = f2.getStringBounds(text, g2.getFontRenderContext());
+ g2.rotate(DrehwinkelGeben (winkel), x + bounds.getWidth() / 2, y - bounds.getHeight() / 2);
+ g.drawString(text, x, y);
+ g2.setTransform(alt);
+ }
+ g.setFont(f);
+ }
+ }
+
+ /**
+ * Oberklasse für alle Elemente einer Figur (Figur, Turtle).
+ */
+ private abstract class FigurenElement
+ {
+ double xe;
+ double ye;
+ double breite;
+ double höhe;
+ Color c;
+ /**
+ * Zeichnet das Figurenelement.
+ * @param g das Grafikobjekt
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ abstract void ElementZeichnen(Graphics2D g, double größe, int x, int y);
+
+ /**
+ * Fügt den Pfadteil deiser Komponente zum umgebenden Pfad hinzu.
+ * @param p der Gesamtpfad
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ abstract void ElementZuForm (Path2D.Double p, double größe, int x, int y);
+ }
+
+ /**
+ * Ein rechteckiges Figurenelement.
+ */
+ private class FigurenElementRechteck extends FigurenElement
+ {
+ /**
+ * Der Konstruktor speichert die Rahmendaten.
+ * @param x x-Koordinate der linken oberen Ecke des Rechtecks relativ zum Aufhängepunkt.
+ * @param y y-Koordinate der linken oberen Ecke des Rechtecks relativ zum Aufhängepunkt.
+ * @param breite Breite des Rechtecks
+ * @param höhe Höhe des Rechtecks
+ * @param c Farbe des Rechtecks
+ */
+ FigurenElementRechteck (double x, double y, double breite, double höhe, Color c)
+ {
+ this.xe = x;
+ this.ye = y;
+ this.breite = breite;
+ this.höhe = höhe;
+ this.c = c;
+ }
+
+ /**
+ * Zeichnet das Figurenelement.
+ * @param g das Grafikobjekt
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ @Override void ElementZeichnen(Graphics2D g, double größe, int x, int y)
+ {
+ g.setColor(c);
+ g.fill(new Rectangle2D.Double (x+größe*xe/100.0, y+größe*ye/100.0, größe*breite/100.0, größe*höhe/100.0));
+ }
+
+ /**
+ * Fügt den Pfadteil dieser Komponente zum umgebenden Pfad hinzu.
+ * @param p der Gesamtpfad
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ @Override void ElementZuForm (Path2D.Double p, double größe, int x, int y)
+ {
+ p.append(new Rectangle2D.Double (x+größe*xe/100.0, y+größe*ye/100.0, größe*breite/100.0, größe*höhe/100.0), false);
+ }
+ }
+
+ /**
+ * Ein elliptisches Figurenelement.
+ */
+ private class FigurenElementEllipse extends FigurenElement
+ {
+ /**
+ * Der Konstruktor speichert die Rahmendaten.
+ * @param x x-Koordinate der linken oberen Ecke des umgebenden Rechtecks relativ zum Aufhängepunkt.
+ * @param y y-Koordinate der linken oberen Ecke des umgebenden Rechtecks relativ zum Aufhängepunkt.
+ * @param breite Breite der Ellipse
+ * @param höhe Höhe der Ellipse
+ * @param c Farbe der Ellipse
+ */
+ FigurenElementEllipse (double x, double y, double breite, double höhe, Color c)
+ {
+ this.xe = x;
+ this.ye = y;
+ this.breite = breite;
+ this.höhe = höhe;
+ this.c = c;
+ }
+
+ /**
+ * Zeichnet das Figurenelement.
+ * @param g das Grafikobjekt
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ @Override void ElementZeichnen(Graphics2D g, double größe, int x, int y)
+ {
+ g.setColor(c);
+ g.fill(new Ellipse2D.Double (x+größe*xe/100.0, y+größe*ye/100.0, größe*breite/100.0, größe*höhe/100.0));
+ }
+
+ /**
+ * Fügt den Pfadteil dieser Komponente zum umgebenden Pfad hinzu.
+ * @param p der Gesamtpfad
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ @Override void ElementZuForm (Path2D.Double p, double größe, int x, int y)
+ {
+ p.append(new Ellipse2D.Double (x+größe*xe/100.0, y+größe*ye/100.0, größe*breite/100.0, größe*höhe/100.0), false);
+ }
+ }
+
+ /**
+ * Ein Figurenelement begrenzt durch das angegebene Polygon.
+ */
+ private class FigurenElementPolygon extends FigurenElement
+ {
+ /** Das Polygonobjekt */
+ private Polygon poly;
+
+ /**
+ * Der Konstruktor speichert die Rahmendaten.
+ * @param x x-Koordinaten der Stützpunkte des Polygons relativ zum Aufhängepunkt.
+ * @param y y-Koordinaten der Stützpunkte des Polygons relativ zum Aufhängepunkt.
+ * @param c Farbe der Polygonfläche
+ */
+ FigurenElementPolygon (int [] x, int[] y, Color c)
+ {
+ int anz = x.length <= y.length ? x.length : y.length;
+ poly = new Polygon (x, y, anz);
+ Rectangle2D bounds = poly.getBounds2D();
+ xe = bounds.getX();
+ ye = bounds.getY();
+ breite = bounds.getWidth();
+ höhe = bounds.getHeight();
+ this.c = c;
+ }
+
+ /**
+ * Zeichnet das Figurenelement.
+ * @param g das Grafikobjekt
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ @Override void ElementZeichnen(Graphics2D g, double größe, int x, int y)
+ {
+ g.setColor(c);
+ AffineTransform at = new AffineTransform(größe/100.0, 0, 0, größe/100.0, x, y);
+ g.fill(new Path2D.Double (poly, at));
+ }
+
+ /**
+ * Fügt den Pfadteil dieser Komponente zum umgebenden Pfad hinzu.
+ * @param p der Gesamtpfad
+ * @param größe die aktuelle Größe der Figur
+ * @param x die x-Koordinate des Aufhängepunkts der Figur
+ * @param y die y-Koordinate des Aufhängepunkts der Figur
+ */
+ @Override void ElementZuForm (Path2D.Double p, double größe, int x, int y)
+ {
+ AffineTransform at = new AffineTransform(größe/100.0, 0, 0, größe/100.0, x, y);
+ Path2D.Double p2 = new Path2D.Double (poly, at);
+ p2.closePath();
+ p2.setWindingRule(Path2D.WIND_EVEN_ODD);
+ p.append(p2, false);
+ }
+ }
+
+ /**
+ * Das Objekt dieser Klasse zeichnet den Weg der Turtle.
+ */
+ class TurtleIntern extends GrafikSymbol
+ {
+ private class LinienElement
+ {
+ /** x-Koordinate des Startpunktes. */
+ private double xStart;
+ /** y-Koordinate des Startpunktes. */
+ private double yStart;
+ /** x-Koordinate des Endpunktes. */
+ private double xEnde;
+ /** y-Koordinate des Endpunktes. */
+ private double yEnde;
+ /** Farbe des LinienElements. */
+ private Color c;
+
+ LinienElement (double xStart, double yStart, double xEnde, double yEnde, Color c)
+ {
+ this.xStart = xStart;
+ this.yStart = yStart;
+ this.xEnde = xEnde;
+ this.yEnde = yEnde;
+ this.c = c;
+ }
+
+ void Zeichnen (Graphics2D g)
+ {
+ g.setColor(c);
+ g.draw(new Line2D.Double (xStart, yStart, xEnde, yEnde));
+ }
+ }
+
+ /**
+ * Verwaltet das Hintergrundfenster für die Turtlezeichnung.
+ */
+ private class HintergrundBild
+ {
+ /** Das aktuelle Hintergrundbild. */
+ private BufferedImage bild;
+ /** Das zugehörige Zeichenobjekt. */
+ private Graphics2D g;
+
+ /**
+ * Der Konstruktor legt das Bild in der Größe der Zeichenfläche an.
+ */
+ HintergrundBild()
+ {
+ bild = new BufferedImage(Zeichenfenster.MalflächenBreiteGeben(), Zeichenfenster.MalflächenBreiteGeben(), BufferedImage.TYPE_INT_ARGB);
+ g = bild.createGraphics();
+ g.setColor(new Color (0, 0, 0, 0));
+ g.fillRect(0, 0, bild.getWidth(), bild.getHeight());
+ }
+
+ /**
+ * Zeichent die angegebe Linie in das Bild.
+ * @param linie das zu zeichnende Linienelement.
+ */
+ void LinieZeichnen(LinienElement linie)
+ {
+ linie.Zeichnen(g);
+ }
+
+ /**
+ * Zeichnet das Bild in das angegebene Zeichenobjekt.
+ * @param wohin Zeichenobjekt
+ */
+ void BildZeichnen (Graphics2D wohin)
+ {
+ wohin.drawImage(bild, null, 0, 0);
+ }
+ }
+
+ /** Genaue x-Koordinate der Turtle. */
+ double xD;
+ /** Genaue y-Koordinate der Turtle. */
+ double yD;
+ /** Startkoordinate der Turtle. */
+ private int homeX;
+ /** Startkoordinate der Turtle. */
+ private int homeY;
+ /** Startwinkel der Turtle. */
+ private int homeWinkel;
+ /** Stiftposition. */
+ boolean stiftUnten;
+ /** Die Sichtbarkeit des Turtle-Symbols. */
+ private boolean symbolSichtbar;
+ /** Linienelemente. */
+ private ArrayList<LinienElement> linien;
+ /** Standardfigur für Turtle. */
+ private LinkedList<FigurenElement> standardFigur;
+ /** Das Hintergrundbild für die Linien. */
+ private HintergrundBild hintergrund;
+
+ /**
+ * Legt die Turtle mit Startpunkt (100|200) in Richtung 0˚ an.
+ */
+ TurtleIntern ()
+ {
+ super ();
+ x = 100;
+ y = 200;
+ xD = x;
+ yD = y;
+ h = 40;
+ b = 40;
+ homeX=x;
+ homeY=y;
+ homeWinkel=winkel;
+ c = Color.black;
+ stiftUnten = true;
+ symbolSichtbar = true;
+ linien = new ArrayList<LinienElement>();
+ hintergrund = new HintergrundBild();
+ standardFigur = new LinkedList<FigurenElement>();
+ StandardfigurErzeugen();
+ FormErzeugen();
+ }
+
+ /**
+ * Baut die Standardfigur aus den Elementen auf.
+ */
+ private void StandardfigurErzeugen()
+ {
+ //Kopf
+ standardFigur.add(new FigurenElementEllipse(50, -12.5, 25, 25, Color.GREEN));
+ //Beine
+ standardFigur.add(new FigurenElementEllipse(22.5, -32.5, 12.5, 17.5, Color.GREEN));
+ standardFigur.add(new FigurenElementEllipse(40.0, -32.5, 12.5, 17.5, Color.GREEN));
+ standardFigur.add(new FigurenElementEllipse(22.5, 15.0, 12.5, 17.5, Color.GREEN));
+ standardFigur.add(new FigurenElementEllipse(40.0, 15.0, 12.5, 17.5, Color.GREEN));
+ //Augen
+ standardFigur.add(new FigurenElementRechteck(67.5, -10.0, 5.0, 7.5, c));
+ standardFigur.add(new FigurenElementRechteck(67.5, 2.5, 5.0, 7.5, c));
+ //Schwanz
+ standardFigur.add(new FigurenElementEllipse(0, -3.75, 25, 7.5, c));
+ //Rumpf
+ standardFigur.add(new FigurenElementEllipse(7.5, -23.75, 57.5, 47.5, braun));
+ }
+
+ /**
+ * Passt das Hintergrundbild an eine neue Größe der Zeichenfläche an.
+ */
+ void NeueGrößeSetzen()
+ {
+ hintergrund = new HintergrundBild();
+ for (LinienElement l: linien)
+ {
+ hintergrund.LinieZeichnen(l);
+ }
+ }
+
+ /**
+ * Erstellt die Form der Turtle.
+ */
+ @Override void FormErzeugen()
+ {
+ Area area = new Area();
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (winkel), this.x, this.y);
+ double größe = h > b ? b : h;
+ if (standardFigur != null)
+ {
+ synchronized (standardFigur)
+ {
+ for (FigurenElement e: standardFigur)
+ {
+ Path2D.Double p = new Path2D.Double();
+ e.ElementZuForm(p, größe, x, y);
+ area.add( new Area(new Path2D.Double (p, a)));
+ }
+ }
+
+ }
+ form = area;
+ }
+
+ /**
+ * Setzt die Position (der linken oberen Ecke) des Objekts.
+ * @param x x-Position der linken oberen Ecke
+ * @param y y-Position der linken oberen Ecke
+ */
+ @Override void PositionSetzen (int x, int y)
+ {
+ super.PositionSetzen (x, y);
+ xD = x;
+ yD = y;
+ }
+
+ /**
+ * Setzt die Turtle wieder an ihre Ausgangsposition.
+ */
+ void ZumStartpunktGehen()
+ {
+ x = homeX;
+ y = homeY;
+ xD = x;
+ yD = y;
+ winkel = homeWinkel;
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Bewegt die Turtle nach vorne.
+ * @param länge Anzahl der Längeneinheiten
+ */
+ void Gehen(double länge)
+ {
+ double neuX = xD + Math.cos(DrehwinkelGeben (winkel)) * länge;
+ double neuY = yD + Math.sin(DrehwinkelGeben (winkel)) * länge;
+ if (stiftUnten)
+ {
+ synchronized (this)
+ {
+ LinienElement l = new LinienElement(xD, yD, neuX, neuY, c);
+ linien.add (l);
+ hintergrund.LinieZeichnen (l);
+ }
+ }
+ xD = neuX;
+ yD = neuY;
+ x =(int) Math.round(xD);
+ y =(int) Math.round(yD);
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Dreht die Turtle
+ * @param grad Drehwinkel im Gradmass
+ */
+ void Drehen(int grad)
+ {
+ winkel = WinkelNormieren(winkel + grad);
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Versetzt Zeichenfläche und Turtle in den Ausgangszustand
+ */
+ void Löschen()
+ {
+ linien.clear();
+ hintergrund = new HintergrundBild();
+ ZumStartpunktGehen();
+ }
+
+ /**
+ * Turtle wechselt in den Modus "nicht zeichnen"
+ */
+ void StiftHeben()
+ {
+ stiftUnten = false;
+ }
+
+ /**
+ * Turtle wechselt in den Modus "zeichnen"
+ */
+ void StiftSenken()
+ {
+ stiftUnten = true;
+ }
+
+ /**
+ * Schaltet die Sichtbarkeit des Turtlesymbols ein oder aus.
+ * Erlaubte Parameterwerte: true, false
+ * @param sichtbar (neue) Sichtbarkeit des Turtlesymbols
+ */
+ void SichtbarkeitFürSymbolSetzen (boolean sichtbar)
+ {
+ symbolSichtbar = sichtbar;
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Testet, ob der angegebene Punkt innerhalb der Figur ist.
+ * @param x x-Koordinate des zu testenden Punktes
+ * @param y y-Koordinate des zu testenden Punktes
+ * @return wahr, wenn der Punkt innerhalb der Figur ist
+ */
+
+ /**
+ * Testet, ob die Turtle eine (sichtbare) Figur berührt.
+ * @return true, wenn die Turtlekoordinaten innerhalb einer Grafikfigur sind
+ */
+ boolean Berührt ()
+ {
+ for (GrafikSymbol g: zeichenfläche.alleSymbole)
+ {
+ if ((g != this) && g.IstInnerhalb(x, y) && g.sichtbar && (!(g instanceof TurtleIntern) || ((TurtleIntern) g).symbolSichtbar))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Testet, ob die Turtle eine (sichtbare) Figur in der angegebenen Farbe berührt.
+ * Bei Überlappungen
+ * @param farbe die Farbe, die die berührte Figur haben muss.
+ * @return true, wenn die Turtlekoordinaten innerhalb einer Grafikfigur in der angegebenen Farbe sind
+ */
+ boolean Berührt (String farbe)
+ {
+ Color c2 = FarbeCodieren(farbe);
+ boolean ok = false;
+ for (GrafikSymbol g: zeichenfläche.alleSymbole)
+ {
+ if ((g != this) && g.IstInnerhalb(x, y) && g.sichtbar)
+ {
+ if (g instanceof TurtleIntern)
+ {
+ TurtleIntern t = (TurtleIntern) g;
+ if (t.symbolSichtbar)
+ {
+ for (FigurenElement e: t.standardFigur)
+ {
+ Path2D.Double p = new Path2D.Double();
+ double größe = t.h > t.b ? t.b : t.h;
+ e.ElementZuForm(p, größe, t.x, t.y);
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (t.winkel), t.x, t.y);
+ p = new Path2D.Double (p, a);
+ if (p.contains(x, y))
+ {
+ ok = c2.equals(e.c);
+ }
+ }
+ }
+ }
+ else if (g instanceof FigurIntern)
+ {
+ FigurIntern t = (FigurIntern) g;
+ LinkedList<FigurenElement> figur = ((t.eigeneFigur == null) || (t.eigeneFigur.size() == 0)) ? t.standardFigur : t.eigeneFigur;
+ for (FigurenElement e: figur)
+ {
+ Path2D.Double p = new Path2D.Double();
+ double größe = t.h > t.b ? t.b : t.h;
+ e.ElementZuForm(p, größe, t.x, t.y);
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (t.winkel), t.x, t.y);
+ p = new Path2D.Double (p, a);
+ if (p.contains(x, y))
+ {
+ ok = c2.equals(e.c);
+ }
+ }
+ }
+ else
+ {
+ ok = ok || c2.equals(g.c);
+ }
+ }
+ }
+ return ok;
+ }
+
+ /**
+ * Testet, ob die Turtle die (sichtbare, ) angegebene Figur berührt.
+ * @param object das Objekt, das getestet werden soll.
+ * @return true, wenn die Turtlekoordinaten innerhalb einer Grafikfigur in der angegebenen Farbe sind
+ */
+ boolean Berührt (Object object)
+ {
+ GrafikSymbol s = null;
+ if (object instanceof Rechteck)
+ {
+ s = ((Rechteck) object).symbol;
+ }
+ else if (object instanceof Dreieck)
+ {
+ s = ((Dreieck) object).symbol;
+ }
+ else if (object instanceof Kreis)
+ {
+ s = ((Kreis) object).symbol;
+ }
+ else if (object instanceof Turtle)
+ {
+ s = ((Turtle) object).symbol;
+ }
+ else if (object instanceof Figur)
+ {
+ s = ((Figur) object).symbol;
+ }
+ return (s != null) && (s != this) && s.IstInnerhalb(x, y) && s.sichtbar && (!(s instanceof TurtleIntern) || ((TurtleIntern) s).symbolSichtbar);
+ }
+
+ /**
+ * Zeichnet das Objekt als Dreieck in der gegebenen Farbe.
+ * @param g das Grafikobjekt zum Zeichnen
+ */
+ @Override void Zeichnen(Graphics g)
+ {
+ Graphics2D g2 = (Graphics2D) g;
+ synchronized (this)
+ {
+ hintergrund.BildZeichnen(g2);
+ }
+
+ if (symbolSichtbar)
+ {
+ g.setColor(Color.black);
+ double größe = h > b ? b : h;
+ AffineTransform alt = g2.getTransform();
+ g2.rotate(DrehwinkelGeben (winkel), x, y);
+ if (standardFigur != null)
+ {
+ synchronized (standardFigur)
+ {
+ for (FigurenElement e: standardFigur)
+ {
+ e.ElementZeichnen(g2, größe, x, y);
+ }
+ }
+ }
+ g2.setTransform(alt);
+ }
+ }
+ }
+
+ /**
+ * Das Objekt dieser Klasse ist ein in der Gestalt definierbarer Akteur.
+ */
+ class FigurIntern extends GrafikSymbol
+ {
+
+ /** Genaue x-Koordinate der Figur. */
+ double xD;
+ /** Genaue y-Koordinate der Figur. */
+ double yD;
+ /** Startkoordinate der Figur. */
+ private int homeX;
+ /** Startkoordinate der Figur. */
+ private int homeY;
+ /** Startwinkel der Figur. */
+ private int homeWinkel;
+ /** Eigene Figur für Figur. */
+ private LinkedList<FigurenElement> eigeneFigur;
+ /** Standardfigur für Figur. */
+ private LinkedList<FigurenElement> standardFigur;
+
+ /**
+ * Legt die Figur mit Startpunkt (100|200) in Richtung 0˚ an.
+ */
+ FigurIntern ()
+ {
+ super ();
+ x = 100;
+ y = 200;
+ xD = x;
+ yD = y;
+ h = 40;
+ b = 40;
+ homeX=x;
+ homeY=y;
+ homeWinkel=winkel;
+ c = Color.black;
+ eigeneFigur = new LinkedList<FigurenElement>();
+ standardFigur = new LinkedList<FigurenElement>();
+ StandardfigurErzeugen();
+ FormErzeugen();
+ }
+
+ /**
+ * Baut die Standardfigur aus den Elementen auf.
+ */
+ private void StandardfigurErzeugen()
+ {
+ int[] x = new int [] {-50, 50, -50};
+ int[] y = new int [] {-50, 0, 50};
+ standardFigur.add (new FigurenElementPolygon (x, y, Color.yellow));
+ standardFigur.add(new FigurenElementEllipse(-10, -10, 20, 20, Color.blue));
+ }
+
+ /**
+ * Erstellt die Form der Figur.
+ */
+ @Override void FormErzeugen()
+ {
+ Area area = new Area();
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (winkel), this.x, this.y);
+ double größe = h > b ? b : h;
+ if (standardFigur != null)
+ {
+ LinkedList<FigurenElement> figur = ((eigeneFigur == null) || (eigeneFigur.size() == 0)) ? standardFigur : eigeneFigur;
+ synchronized (figur)
+ {
+ for (FigurenElement e: figur)
+ {
+ Path2D.Double p = new Path2D.Double();
+ e.ElementZuForm(p, größe, x, y);
+ area.add(new Area(new Path2D.Double (p, a)));
+ }
+ }
+
+ }
+ form = area;
+ }
+
+ /**
+ * Setzt die Position (der Mitte) des Objekts.
+ * @param x x-Position der Mitte
+ * @param y y-Position der Mitte
+ */
+ @Override void PositionSetzen (int x, int y)
+ {
+ super.PositionSetzen (x, y);
+ xD = x;
+ yD = y;
+ }
+
+ /**
+ * Setzt die Figur wieder an ihre Ausgangsposition.
+ */
+ void ZumStartpunktGehen()
+ {
+ x = homeX;
+ y = homeY;
+ xD = x;
+ yD = y;
+ winkel = homeWinkel;
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Bewegt die Figur nach vorne.
+ * @param länge Anzahl der Längeneinheiten
+ */
+ void Gehen(double länge)
+ {
+ double neuX = xD + Math.cos(DrehwinkelGeben (winkel)) * länge;
+ double neuY = yD + Math.sin(DrehwinkelGeben (winkel)) * länge;
+ xD = neuX;
+ yD = neuY;
+ x =(int) Math.round(xD);
+ y =(int) Math.round(yD);
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Dreht die Figur
+ * @param grad Drehwinkel im Gradmass
+ */
+ void Drehen(int grad)
+ {
+ winkel = WinkelNormieren(winkel + grad);
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Testet, ob die Figur eine (sichtbare) Grafik-Figur berührt.
+ * @return true, wenn die Figurkoordinaten innerhalb einer Grafikfigur sind
+ */
+ boolean Berührt ()
+ {
+ for (GrafikSymbol g: zeichenfläche.alleSymbole)
+ {
+ if ((g != this) && g.Schneidet(form) && g.sichtbar && (!(g instanceof TurtleIntern) || ((TurtleIntern) g).symbolSichtbar))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Testet, ob die Figur eine (sichtbare) Grafik-Figur in der angegebenen Farbe berührt.
+ * Bei Überlappungen
+ * @param farbe die Farbe, die die berührte Figur haben muss.
+ * @return true, wenn die Figurkoordinaten innerhalb einer Grafikfigur in der angegebenen Farbe sind
+ */
+ boolean Berührt (String farbe)
+ {
+ Color c2 = FarbeCodieren(farbe);
+ boolean ok = false;
+ for (GrafikSymbol g: zeichenfläche.alleSymbole)
+ {
+ if ((g != this) && g.Schneidet(form) && g.sichtbar)
+ {
+ if (g instanceof TurtleIntern)
+ {
+ TurtleIntern t = (TurtleIntern) g;
+ if (t.symbolSichtbar)
+ {
+ Area[] areas = new Area [t.standardFigur.size()];
+ Color[] colors = new Color [t.standardFigur.size()];
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (t.winkel), t.x, t.y);
+ int pos = 0;
+ for (FigurenElement e: t.standardFigur)
+ {
+ Path2D.Double p = new Path2D.Double();
+ double größe = t.h > t.b ? t.b : t.h;
+ e.ElementZuForm(p, größe, t.x, t.y);
+ p = new Path2D.Double (p, a);
+ areas [pos] = new Area(p);
+ colors[pos] = e.c;
+ for (int i = pos - 1; i >= 0;i--)
+ {
+ areas[i].subtract(areas[pos]);
+ }
+ pos += 1;
+ }
+ for (int i = 0; i < areas.length; i++)
+ {
+ if (Schneidet(areas[i]) && (c2.equals(colors[i])))
+ {
+ ok = true;
+ }
+ }
+ }
+ }
+ else if (g instanceof FigurIntern)
+ {
+ FigurIntern t = (FigurIntern) g;
+ LinkedList<FigurenElement> figur = ((t.eigeneFigur == null) || (t.eigeneFigur.size() == 0)) ? t.standardFigur : t.eigeneFigur;
+ Area[] areas = new Area [figur.size()];
+ Color[] colors = new Color [figur.size()];
+ AffineTransform a = new AffineTransform();
+ a.rotate(DrehwinkelGeben (t.winkel), t.x, t.y);
+ int pos = 0;
+ for (FigurenElement e: figur)
+ {
+ Path2D.Double p = new Path2D.Double();
+ double größe = t.h > t.b ? t.b : t.h;
+ e.ElementZuForm(p, größe, t.x, t.y);
+ p = new Path2D.Double (p, a);
+ areas [pos] = new Area(p);
+ colors[pos] = e.c;
+ for (int i = pos - 1; i >= 0;i--)
+ {
+ areas[i].subtract(areas[pos]);
+ }
+ pos += 1;
+ }
+ for (int i = 0; i < areas.length; i++)
+ {
+ if (Schneidet(areas[i]) && (c2.equals(colors[i])))
+ {
+ ok = true;
+ }
+ }
+ }
+ else
+ {
+ ok = ok || c2.equals(g.c);
+ }
+ }
+ }
+ return ok;
+ }
+
+ /**
+ * Testet, ob die Figur die (sichtbare, ) angegebene Grafik-Figur berührt.
+ * @param object das Objekt, das getestet werden soll.
+ * @return true, wenn die Turtlekoordinaten innerhalb einer Grafikfigur in der angegebenen Farbe sind
+ */
+ boolean Berührt (Object object)
+ {
+ GrafikSymbol s = null;
+ if (object instanceof Rechteck)
+ {
+ s = ((Rechteck) object).symbol;
+ }
+ else if (object instanceof Dreieck)
+ {
+ s = ((Dreieck) object).symbol;
+ }
+ else if (object instanceof Kreis)
+ {
+ s = ((Kreis) object).symbol;
+ }
+ else if (object instanceof Turtle)
+ {
+ s = ((Turtle) object).symbol;
+ }
+ else if (object instanceof Figur)
+ {
+ s = ((Figur) object).symbol;
+ }
+ return (s != null) && (s != this) && s.Schneidet(form) && s.sichtbar && (!(s instanceof TurtleIntern) || ((TurtleIntern) s).symbolSichtbar);
+ }
+
+ /**
+ * Erzeugt ein neues, rechteckiges Element einer eigenen Darstellung der Figur.
+ * Alle Werte beziehen sich auf eine Figur der Größe 100 und den Koordinaten (0|0) in der Mitte des Quadrats
+ * @param x x-Wert der linken oberen Ecke des Rechtecks
+ * @param y y-Wert der linken oberen Ecke des Rechtecks
+ * @param breite Breite des Rechtecks
+ * @param höhe Höhe des Rechtecks
+ * @param farbe (Füll)Farbe des Rechtecks
+ */
+ void FigurteilFestlegenRechteck (int x, int y, int breite, int höhe, String farbe)
+ {
+ synchronized (eigeneFigur)
+ {
+ eigeneFigur.add(new FigurenElementRechteck(x, y, breite, höhe, FarbeCodieren(farbe)));
+ }
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Erzeugt ein neues, elliptisches Element einer eigenen Darstellung der Figur.
+ * Alle Werte beziehen sich auf eine Figur der Größe 100 und den Koordinaten (0|0) in der Mitte des Quadrats
+ * @param x x-Wert der linken oberen Ecke des umgebenden Rechtecks der Ellipse
+ * @param y y-Wert der linken oberen Ecke des umgebenden Rechtecks der Ellipse
+ * @param breite Breite des umgebenden Rechtecks der Ellipse
+ * @param höhe Höhe des umgebenden Rechtecks der Ellipse
+ * @param farbe (Füll)Farbe der Ellipse
+ */
+ void FigurteilFestlegenEllipse (int x, int y, int breite, int höhe, String farbe)
+ {
+ synchronized (eigeneFigur)
+ {
+ eigeneFigur.add(new FigurenElementEllipse(x, y, breite, höhe, FarbeCodieren(farbe)));
+ }
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Erzeugt ein neues, dreieckiges Element einer eigenen Darstellung der Figur.
+ * Alle Werte beziehen sich auf eine Figur der Größe 100 und den Koordinaten (0|0) in der Mitte des Quadrats
+ * @param x1 x-Wert des ersten Punkts des Dreiecks
+ * @param y1 y-Wert des ersten Punkts des Dreiecks
+ * @param x2 x-Wert des zweiten Punkts des Dreiecks
+ * @param y2 y-Wert des zweiten Punkts des Dreiecks
+ * @param x3 x-Wert des dritten Punkts des Dreiecks
+ * @param y3 y-Wert des dritten Punkts des Dreiecks
+ * @param farbe (Füll)Farbe der Ellipse
+ */
+ void FigurteilFestlegenDreieck (int x1, int y1, int x2, int y2, int x3, int y3, String farbe)
+ {
+ synchronized (eigeneFigur)
+ {
+ int[] x = new int [] {x1, x2, x3};
+ int[] y = new int [] {y1, y2, y3};
+ eigeneFigur.add(new FigurenElementPolygon(x, y, FarbeCodieren(farbe)));
+ }
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Löscht die Vereinbarung für die eigene Darstellung Figur.
+ * Die Figur wird wieder durch die Originalfigur dargestellt.
+ */
+ void EigeneFigurLöschen()
+ {
+ eigeneFigur.clear();
+ FormErzeugen();
+ zeichenfläche.malfläche.repaint();
+ }
+
+ /**
+ * Zeichnet das Objekt als Dreieck in der gegebenen Farbe.
+ * @param g das Grafikobjekt zum Zeichnen
+ */
+ @Override void Zeichnen(Graphics g)
+ {
+ Graphics2D g2 = (Graphics2D) g;
+ // Outline
+ g.setColor(Color.black);
+ Stroke stAlt = g2.getStroke();
+ g2.setStroke(new BasicStroke(3.0f));
+ g2.draw(form);
+ g2.setStroke(stAlt);
+ // Füllung
+ double größe = h > b ? b : h;
+ AffineTransform alt = g2.getTransform();
+ g2.rotate(DrehwinkelGeben (winkel), x, y);
+ if (standardFigur != null)
+ {
+ LinkedList<FigurenElement> figur = ((eigeneFigur == null) || (eigeneFigur.size() == 0)) ? standardFigur : eigeneFigur;
+ synchronized (figur)
+ {
+ for (FigurenElement e: figur)
+ {
+ e.ElementZeichnen(g2, größe, x, y);
+ }
+ }
+ }
+ g2.setTransform(alt);
+ }
+ }
+}