/** Der Herbert ist los! * Dieser Hamster hat die Fähigkeit beliebige Drehungen zu machen, ist also * nicht an ein Gitternetz gebunden * Es ist möglich den Hamster beliebig viele Bewegungsabläufe wiederholen zu * lassen * Der Hamster hat auch gelernt, wie man tolle Muster laufen kann: * Die T-Struktur, das Sierpinski-Dreieck, eine Schneeflocke und * den Pythagoras-Baum. * * @author ( (c) Thilo Beckmann ) * @version ( 16.03.2004 ) * **/ import java.applet.*; import java.awt.event.*; import java.awt.*; public class fraktal extends Applet implements ActionListener, ItemListener { Hamster Herbert = new Hamster(); // Zeichenfläche Hamster Vorschau = new Hamster(); // Richtungsvorschau TextField schritt; // Für die Weite des Schrittes zuständig TextField winkel; // Für den Winkel zuständig TextField wholung; // Für die Wiederholung der eingestellten schrittweite und winkel TextField verzoeg; // Für die zeitliche Verzögerung TextField laenge; // Fraktalanfangslänge TextField tiefe; // FraktalRekursionstiefe Label laengel; // Beschriftung Label tiefel; // Beschriftung Label lab1; // Überschrift für die Einstellungen Label lab2; // Unter-Überschrift für die Einstellungen Label lab3; // Unter-Überschrift für die Einstellungen Label schrittl; // Beschriftung für die gemachten Schritte Label winkell; // Beschriftung für die momentane Ausrichtung Label verzoegl; // Zum Einschalten der zeitlichen Verzögerung Label wholungl; // Für die Anzahl der Umläufe bis zur ursprünglichen Orientierung Label wholunglinfo; // Information zu Wiederholung Label koordina; // Anzeige der Koordinaten Label info; // Für Copyright etc... Button wholungb; // Klicken, Hamster führt eingestellte Bewegung aus Button schrittb; // Klicken, Hamster läuft vorwärts Button winkelb; // Klicken, Hamster dreht sich nach links Button wholungspez; // Klicken, Hamster läuft solange, bis er die alte Orient. hat Button bildLoeschen;// Klicken, um das Feld zu löschen Button fraktalb; // Klicken, um das ausgewählte Fraktal zu zeichnen Label fraktall; // Namen, des ausgewählten Fraktals Checkbox fraktalcb1;// Zum Auswählen der Fraktale Checkbox fraktalcb2, fraktalcb3, fraktalcb4, fraktalcb5; Checkbox malen; // Markieren, damit der Hamster eine Spur hinterlässt String s; // Zum Übertragen der Eingegebenen Werte int schritti = 0; // Anzahl der gelaufenen Schritte int winkeli = 0; // Aktuelle Orientierung in Grad (0=rechts) int schritteBis360i = 0; // Anzahl Wiederholungen für "vollen" Igel int schrittialt = 0;// alter Wert für schritteBis360i int b=100; // Breite des Hamsterfeldes int h=100; // Hoehe des Hamsterfeldes int r=5; // Rand um Hamsterfeld CheckboxGroup cbg = new CheckboxGroup(); // Für die Anwahl der versch. Fraktale public void init () { b = getWidth() - 2*r - 160; h = getHeight()- 2*r - 30; this.setFont(new Font("Arial", Font.PLAIN, 12)); this.setBackground(Color.lightGray); setLayout(null); // Initialisierung der Steuerelemente: lab1 = new Label("Einstellungen:"); lab1.setFont(new Font("Arial", Font.BOLD, 20)); lab2 = new Label("Fraktale zeichnen:"); lab2.setFont(new Font("Arial", Font.BOLD, 12)); lab3 = new Label("Info über den Hamster:"); lab3.setFont(new Font("Arial", Font.BOLD, 12)); schritt = new TextField("300", 3); schrittb = new Button("vorwärts"); schrittl = new Label("Schritte gelaufen: 0"); winkel = new TextField("130", 3); winkelb = new Button("° drehen"); winkell = new Label("Richtung: 0° "); bildLoeschen= new Button("Bild löschen"); malen = new Checkbox("Spur hinterlassen", null, true); wholung = new TextField("36", 3); wholungb = new Button("mal gehen"); wholungspez = new Button("zeichne Igel"); wholungl = new Label("0 Runden"); wholunglinfo= new Label("Ein Igel mit 130° braucht:"); verzoeg = new TextField("0",3); verzoegl = new Label("msec. Verzög."); info = new Label("(c) Thilo Beckmann 16.03.04"); koordina = new Label("x: 0 | y: 0"); fraktalb = new Button("zeichne"); fraktall = new Label("Sierpinski-Dreieck"); fraktalcb1 = new Checkbox("1",cbg,false); fraktalcb1.setForeground(Color.lightGray); fraktalcb2 = new Checkbox("2",cbg,false); fraktalcb2.setForeground(Color.lightGray); fraktalcb3 = new Checkbox("3",cbg,true); fraktalcb3.setForeground(Color.lightGray); fraktalcb4 = new Checkbox("4",cbg,false); fraktalcb4.setForeground(Color.lightGray); fraktalcb5 = new Checkbox("5",cbg,false); fraktalcb5.setForeground(Color.lightGray); laenge = new TextField("150",3); laengel = new Label("Länge"); tiefe = new TextField("5",3); tiefel = new Label("Tiefe"); // Initialisierung von Herbert: Herbert.setSize(b,h); Herbert.setBackground(Color.white); Herbert.setBounds(r,r,b,h); add(Herbert); Herbert.init(); Vorschau.setSize(65,60); Vorschau.setBackground(Color.lightGray); Vorschau.setBounds(b+2*r+90,230,65,60); Vorschau.setStartPos(33.0,30.0); Vorschau.setBeobachtung(false); add(Vorschau); Vorschau.init(); groesseAngleichen(); add(lab1); add(lab2); add(lab3); add(verzoeg); add(verzoegl); add(schritt); add(schrittb); schrittb.addActionListener(this); add(winkel); add(winkelb); winkelb.addActionListener(this); add(schrittl); add(winkell); add(koordina); add(wholung); add(wholungb); wholungb.addActionListener(this); add(wholunglinfo); add(wholungl); add(wholungspez); wholungspez.addActionListener(this); add(malen); add(bildLoeschen); bildLoeschen.addActionListener(this); add(info); add(fraktalb); fraktalb.addActionListener(this); add(fraktall); add(fraktalcb1); fraktalcb1.addItemListener(this); add(fraktalcb2); fraktalcb2.addItemListener(this); add(fraktalcb3); fraktalcb3.addItemListener(this); add(fraktalcb4); fraktalcb4.addItemListener(this); add(fraktalcb5); fraktalcb5.addItemListener(this); add(laenge); add(laengel); add(tiefe); add(tiefel); } public void paint(Graphics g) { groesseAngleichen(); } public void groesseAngleichen() { b = getWidth() - 2*r - 160; h = getHeight()- 2*r - 30; Herbert.setBounds(r,r,b,h); // Positionierung der Steuerelemente lab1. setBounds(b+2*r, 0,150,30); verzoeg. setBounds(b+2*r, 40, 50,25); verzoegl. setBounds(b+2*r+70, 40, 80,25); schritt. setBounds(b+2*r, 65, 50,25); schrittb. setBounds(b+2*r+70, 65, 80,25); winkel. setBounds(b+2*r, 90, 50,25); winkelb. setBounds(b+2*r+70, 90, 80,25); wholung. setBounds(b+2*r, 115, 50,25); wholungb. setBounds(b+2*r+70, 115, 80,25); wholunglinfo. setBounds(b+2*r, 140,150,25); wholungl. setBounds(b+2*r, 165, 70,25); wholungspez. setBounds(b+2*r+70, 165, 80,25); lab2. setBounds(b+2*r, 200,150,25); fraktalcb1. setBounds(b+2*r, 225, 15,25); fraktalcb2. setBounds(b+2*r+15, 225, 15,25); fraktalcb3. setBounds(b+2*r+30, 225, 15,25); fraktalcb4. setBounds(b+2*r+45, 225, 15,25); fraktalcb5. setBounds(b+2*r+60, 225, 15,25); fraktalb. setBounds(b+2*r+80, 225, 60,25); fraktall. setBounds(b+2*r, 250,150,25); laenge. setBounds(b+2*r, 275, 30,25); laengel. setBounds(b+2*r+30, 275, 45,25); tiefe. setBounds(b+2*r+75, 275, 30,25); tiefel. setBounds(b+2*r+105,275, 45,25); lab3. setBounds(b+2*r, 300,150,30); schrittl. setBounds(b+2*r, 325,150,30); schrittl.setBackground(Color.lightGray); winkell. setBounds(b+2*r, 350, 85,30); winkell. setBackground(Color.lightGray); Vorschau. setBounds(b+2*r+90, 350, 65,60); koordina. setBounds(b+2*r, 375, 85,30); koordina.setBackground(Color.lightGray); malen. setBounds(b+2*r, 405,150,25); bildLoeschen. setBounds(b+2*r, 430,150,30); info. setBounds(10,h+2*r, 400,30); Herbert.zeichneRahmen(); Vorschau.zeichneRahmen(); } public int laengePause() { if (verzoeg.getText() != "") { return Integer.parseInt(verzoeg.getText()); } else { verzoeg.setText("0"); return 0; } } public void itemStateChanged(ItemEvent e) { String cblabel = (cbg.getSelectedCheckbox()).getLabel(); if (cblabel == "1") { fraktall.setText("T-Fraktal"); tiefe.setBackground(Color.white); } else if (cblabel =="2") { fraktall.setText("Schneeflocke"); tiefe.setBackground(Color.lightGray); } else if (cblabel =="3") { fraktall.setText("Sierpinski-Dreieck"); tiefe.setBackground(Color.white); } else if (cblabel =="4") { fraktall.setText("Pythagoras-Baum"); tiefe.setBackground(Color.white); } else if (cblabel =="5") { fraktall.setText("Koch-Kurve"); tiefe.setBackground(Color.lightGray); } } public void zeichneFraktal() { String auswahl = fraktall.getText(); int ilaenge = Integer.parseInt(laenge.getText()); int itiefe = Integer.parseInt(tiefe.getText()); if (auswahl =="T-Fraktal") { Herbert.fraktal1(ilaenge,itiefe); } else if (auswahl =="Schneeflocke") { for (int i=0; i<6; i++) { Herbert.fraktal2(ilaenge); Herbert.drehe(-60); } } else if (auswahl =="Sierpinski-Dreieck") { Herbert.fraktal3(ilaenge,itiefe); } else if (auswahl =="Pythagoras-Baum") { Herbert.fraktal4(ilaenge,30,itiefe); } else if (auswahl =="Koch-Kurve") { Herbert.fraktal5(ilaenge); } } public void actionPerformed(ActionEvent e) { if (e.getActionCommand() == "vorwärts") { s = schritt.getText(); Herbert.geheVor(Integer.parseInt(s),true); } if (e.getActionCommand() == "° drehen") { s = winkel.getText(); Herbert.dreheZeiger(Integer.parseInt(s)); Vorschau.setOrientierung(winkeli); Vorschau.dreheZeigerBig(Integer.parseInt(s)); } if (e.getActionCommand() == "Bild löschen") { Herbert.allesLoeschen(); } if (e.getActionCommand() == "mal gehen") { igelZeichnen(Integer.parseInt(wholung.getText())); } // Es wird ein Igel gezeichnet if (e.getActionCommand() == "zeichne Igel") { igelZeichnen(schritteBisStart()); } // Das ausgewählte Fraktal wird gezeichnet... if (e.getActionCommand() == "zeichne") { zeichneFraktal(); } aktualisieren(); } public void igelZeichnen(int anzahlwiederholungen) { int schrittweite = Integer.parseInt(schritt.getText()); int grad = Integer.parseInt(winkel.getText()); int w=winkeli + grad; int zuMachendeSchritte = anzahlwiederholungen; if (schrittialt == zuMachendeSchritte) { for (int k=0; k 50) { Vorschau.setOrientierung(winkeli); Vorschau.dreheZeigerBig(grad); aktualisieren(); pause(laengePause()); } } } schrittialt = zuMachendeSchritte; } public void aktualisieren () { // Die Informations-Felder werden aktualisiert: schrittl.setText ("Schritte gelaufen: " + schritti); winkell. setText ("Richtung: " + winkeli+ "°"); koordina.setText ("x: "+(int)Herbert.xpos + " | y: "+(int)Herbert.ypos); // Der Rahmen wird nachgezogen Herbert.zeichneRahmen(); Herbert.dreheZeiger(0); Vorschau.setOrientierung(winkeli); Vorschau.dreheZeigerBig(0); } public void aktualisierenVorschau() { Vorschau.setOrientierung(winkeli); } // Zum Ausrechnen, wieviele Schritte gemacht werden müssen, um wieder // die alte Orientierung zu haben public int schritteBisStart () { int grad = Integer.parseInt(winkel.getText()); int w=Math.abs(winkeli)+grad; int i=1; while (w%360 != Math.abs(winkeli) ) { w = w + grad; i++; if (i>360) break; // Weil nur "ganze" Winkel eingegeben werden // können ist mit 360 spätestens Schluss. } wholunglinfo.setText("Ein Igel mit "+grad+"° braucht:"); wholungl.setText(""+i+" Schritte"); return i; } // Pause-Methode zum Zeitverzögern (in MilliSekunden) public void pause (int dauer) { try { Thread.sleep(dauer); } catch(InterruptedException e) { return; } } /********************************************************************************/ /** Die Hamster-Klasse, in der alle Methoden declariert sind, die man benötigt */ /** um den Hamster zu steuern und die Malfläche anzusteuern. */ /********************************************************************************/ public class Hamster extends Canvas implements MouseMotionListener, MouseListener { Graphics g; double xpos=50.0, ypos=275.0; // die x- und y-Position des Pfeiles double alphaInRad = 0; // der eingestellte Winkel im Bogenmaß int alphaInGrad = 0; // der eingestellte Winkel in Grad int xstart=0, ystart=0; // Zum Festlegen der Richtung mit der Maus int xalt=0, yalt=0; // Zum Löschen des Richtungs-Striches boolean mouseGedrueckt = false; // Ist eine Maustaste gedrückt? boolean mouseBeobachtung = true;// Soll die Maus die Richtung verändern können? // Der Konstruktor des "Hamsters" public Hamster () { addMouseMotionListener(this); // Für die Bewegung der Maus addMouseListener(this); // Für die Tasten der Maus } // Initialisierung des Hamsters public void init() { g = getGraphics(); } // Methode, die den Startpunkt neu festlegt public void setStartPos (double xstartpos, double ystartpos) { xpos = xstartpos; ypos = ystartpos; koordina.setText ("x: "+(int)xpos + " | y: "+(int)ypos); } public void setOrientierung (int neuerWinkel) { alphaInGrad = neuerWinkel; alphaInRad = ((double)(alphaInGrad)) * Math.PI/ 180.0; dreheZeigerBig(0); } public void setBeobachtung (boolean beobachtung) { mouseBeobachtung = beobachtung; } /** Methoden, die durch den MouseMotionListener erzwungen werden: */ /** mouseMoved und mouseDragged */ public void mouseMoved(MouseEvent e) { } // Mit der Maus kann man den Startpunkt des Hamsters neu festlegen // sowie die Orientierung neu bestimmen. public void mouseDragged(MouseEvent e) { if (mouseBeobachtung) { int x = e.getX(); int y = e.getY(); if (mouseGedrueckt) { zeiger(Color.white,5); g.setColor(Color.white); g.drawLine(xstart,ystart,xalt,yalt); g.setColor(Color.black); g.drawLine(xstart,ystart,x,y); xalt = x; yalt = y; zeiger(Color.white,5); winkeli = (int)((Math.atan2(-(ystart-y),xstart-x)*180)/Math.PI)+180; winkell.setText("Richtung: "+winkeli+"°"); zeiger(Color.red,5); } aktualisierenVorschau(); } } /** Methoden, die durch den MouseListener erzwungen werden: */ /** mouseClicked, mouseEntered, mouseExited, mousePressed und*/ /** mouseReleased */ // Der Hamster wird auf die Stelle, an die der Mauszeiger zeigt, gesetzt public void mouseClicked(MouseEvent e) { if (mouseBeobachtung) { int x=e.getX(), y=e.getY(); zeiger(Color.white,5); setStartPos((double)x,(double)y); zeiger(Color.red,5); } } public void mouseEntered(MouseEvent e) { zeichneRahmen(); geheVor(0,true); } public void mouseExited(MouseEvent e) { zeichneRahmen(); geheVor(0,true); } // Wenn eine Maustaste gedrückt wird, dann kann die neue Orientierung // durch ziehen der Maus eingestellt werden public void mousePressed(MouseEvent e) { if (mouseBeobachtung) { int x=e.getX(), y=e.getY(); zeiger(Color.white,5); zeiger(Color.red,5); mouseGedrueckt = true; xstart = (int)xpos; ystart = (int)ypos; } } // Wenn die Maustaste wieder losgelassen wird, dann wird die neue // Orientierung des Hamsters festgelegt und der "Zieh"-Modus beendet public void mouseReleased(MouseEvent e) { if (mouseBeobachtung) { int x=e.getX(), y=e.getY(); mouseGedrueckt = false; // Festlegung der neuen Richtung: g.setColor(Color.white); g.drawLine(xstart,ystart,x,y); zeiger(Color.white,5); winkeli = (int)((Math.atan2(-(ystart-y),xstart-x)*180)/Math.PI)+180; if (xstart==x & ystart==y) { winkeli = alphaInGrad; } winkell.setText("Richtung: "+winkeli+"°"); zeiger(Color.red,5); } } // Methode, die den Pfeil um den angegebenen Weg unter Berücksichtigung // der Richtung nach vorne laufen lässt und dabei eine Spur hinterlässt, // die der angegebenen Farbe entspricht. public void move(int weg, Color farbe) { g.setColor(farbe); double xweg = (weg*Math.cos(alphaInRad)), yweg = (weg*Math.sin(alphaInRad)); g.drawLine ((int)xpos, (int)ypos, (int)(xpos + xweg), (int)(ypos - yweg)); xpos = xpos + xweg; ypos = ypos - yweg; } // Methode, die den Pfeil um den angegebenen Weg unter Berücksichtigung // der Richtung nach vorne laufen lässt. public void geheVor(int weg, boolean mitZeiger) { if (mitZeiger) zeiger(Color.white,5); if (malen.getState()) move(weg, Color.black); // Weg zeichnen else move(weg, Color.white); // Weg loeschen schritti = schritti + weg; if (mitZeiger) zeiger(Color.red,5); } // Umrechnung des eigegebenen Winkels ins Bogenmaß public void drehe(int winkel) { if (mouseGedrueckt) { alphaInGrad = winkeli; } else winkeli = alphaInGrad; alphaInGrad=(alphaInGrad + winkel) % 360; alphaInRad = ((double)(alphaInGrad)) * Math.PI/ 180.0; winkeli = alphaInGrad; } // Methode, die die Richtung des Pfeiles um den angegebenen Winkel // nach links dreht. Dabei wird der alte Hamsterkopf gelöscht, der // neue Winkel eingestellt und der neue Hamsterkopf gezeichnet. public void dreheZeiger(int winkel) { zeiger(Color.white,5); drehe(winkel); zeiger(Color.red,5); } // Für die Vorschau wird der Hamsterkopf "groß" gezeichnet public void dreheZeigerBig(int winkel) { g.setColor(Color.lightGray); g.fillRect(1,1,63,58); drehe(winkel); zeiger(Color.red,15); } // Der Hamsterkopf: public void zeiger (Color f, int weite) { drehe(150); move(weite, f); drehe(180); move(weite,f); drehe(-120); move(weite,f); drehe(180); move(weite,f); drehe(-30); } // Die gesamte Fläche wird gelöscht public void allesLoeschen () { g.setColor(Color.white); g.fillRect(0,0,getWidth(),getHeight()); } // Zeichnet einen schwarzen Rahmen um die Hamsterfläche public void zeichneRahmen() { int weite = getWidth(); int hoehe = getHeight(); g.setColor(Color.black); g.drawRect(0,0,weite-1,hoehe-1); } // Das T-Fraktal public void fraktal1 (int ilaenge, int itiefe) { pause(laengePause()); if (itiefe!=0) { geheVor(ilaenge,false); drehe(90); geheVor(ilaenge,false); drehe(-90); fraktal1(ilaenge/2,itiefe-1); drehe(90); geheVor(-2*ilaenge,false); drehe(-90); fraktal1(ilaenge/2,itiefe-1); drehe(90); geheVor(ilaenge,false); drehe(-90); geheVor(-ilaenge,false); } } // Die Schneeflocke public void fraktal2 (int ilaenge) { pause(laengePause()); if (ilaenge > 5) { ilaenge=ilaenge/3; fraktal2(ilaenge); drehe(60); fraktal2(ilaenge); drehe(-120); fraktal2(ilaenge); drehe(60); fraktal2(ilaenge); } else { geheVor(ilaenge,false); } } // Das Sierpinski-Dreieck public void fraktal3 (int ilaenge, int itiefe) { pause(laengePause()); if (itiefe!=0) { g.setColor(Color.black); geheVor(ilaenge, false);fraktal3(ilaenge/2, itiefe-1);drehe(180); geheVor(ilaenge, false);drehe(60); geheVor(ilaenge, false);fraktal3(ilaenge/2, itiefe-1);drehe(180); geheVor(ilaenge, false);drehe(60); geheVor(ilaenge, false);fraktal3(ilaenge/2, itiefe-1);drehe(180); geheVor(ilaenge, false);drehe(60); } } // Der Brocoli (Pythagoras-Baum) public void fraktal4 (double ilaenge, double winkelwert, int itiefe) { //pause(laengePause()); if (itiefe!=0) { double alpha = winkelwert * Math.PI/ 180.0; double ankathete = Math.cos(alpha) * ilaenge; double gegenkathete = Math.sin(alpha) * ilaenge; double posx=xpos, posy=ypos; // Für Fehlerbegrenzung geheVor((int)ilaenge,false);drehe(300); posx=xpos; posy=ypos; fraktal4(gegenkathete,winkelwert,itiefe-1); drehe(90); xpos=posx; ypos=posy; geheVor((int)gegenkathete,false); posx=xpos; posy=ypos; fraktal4(ankathete,winkelwert,itiefe-1); xpos=posx; ypos=posy; drehe(90); geheVor((int)ankathete,false); drehe(150); posx=xpos; posy=ypos; geheVor((int)ilaenge,false);drehe(270); xpos=posx; ypos=posy; geheVor((int)ilaenge,false);drehe(90); geheVor((int)ilaenge,false);drehe(90); } } // Die KochKurve public void fraktal5 (int ilaenge) { pause(laengePause()); if (ilaenge > 5) { ilaenge=ilaenge/3; fraktal5(ilaenge); drehe(90); fraktal5(ilaenge); drehe(270); fraktal5(ilaenge); drehe(270); fraktal5(ilaenge); drehe(90); fraktal5(ilaenge); } else { geheVor(ilaenge,false); } } } }