import java.awt.*;        
import java.awt.event.*;
import java.lang.System;
import java.lang.Math;

//*** Das Applet erzeugt Fraktale, deren Daten der Zufallsgenerator ***
//*** erstellt. *** Peter Kraus  Juni/Juli 1999 ***********************
//*** Leichte Änderungen von Ulrich Schwebinghaus Juli 2000 ***
//*** Peter Kraus ergänzt Maussteuerung im August 2000 ***
//*** Weitere Modifikationen von U. Schwebinghaus im August 2000 ****

public class astfrak extends java.applet.Applet implements MouseListener, MouseMotionListener
{
   Button cl,cr,cw,cf,cm;   //*** Zunächst Elemente der Oberfläche ********
   Choice fw;
   Panel npan;
   Panel chPan=new Panel();
   Waechter ew=new Waechter();
   Color buttonC=new Color(200,200,200);
   int imax=2;                      //*** Daten : Zahl der Äste, zunächst 2 *****
   double winkel[]=new double[6]; //*** Winkel der Äste zur Senkrechten
   double laenge[]=new double[6]; //*** Länge der Äste ************
   double gr_min;                 //*** Bei dieser Astlänge Abbruch!! *********

   double xo,yo,xu,yu;      //*** Ermittlg d. Ausdehnung in Testphase ****
   double x0=200,dxp=20;    //*** Anfangsdaten Transformation ***
   double y0=200,dyp=20;    //*** double --> Bildschirmpixel ****

   boolean test;          //*** Testphase oder Zeichenphase ***********
   boolean firstrun;      //*** Erster Durchlauf oder nicht ***
   boolean eingabe=false; //*** Neues Fraktal per Maus eingeben oder zufällig ***
   int farbset;    //*** Farbset Nr. 0,1,2,3,4,5,6,7 ***
   
   int i_eingabe=0;            //*** Alles für die Maus-Eingabe ***
   int x_eingabe[]=new int[6];
   int y_eingabe[]=new int[6];
   
   Graphics g;

   long zeitstart,zeitende; //*** Ermittlung der Dauer in der Testphase *

   Font f=new Font("Arial",Font.PLAIN,16);

   Frame w;              //*** Zur Ausgabe der Grundfigur ************

   public int trafo_x(double x)
   {               
      //**** Transformation double --> Bildschirmpixel ****
      int xp=(int)(x*dxp+x0);
      return xp;
   }
   
   public int trafo_y(double y)
   {                 
      //**** Transformation double --> Bildschirmpixel ****
      int yp=(int)(y*dxp+y0);
      return yp;
   }

   public void werte_normieren()
   {  
     //*** Alle Längen auf 1 normieren *******************************
     double laengemax=0;
     for(int i=0;i<imax;i++)
     { 
       //*** Die längste Länge ermitteln ***
       if(laenge[i]>laengemax) laengemax=laenge[i];
     }
     if(laengemax>0) for(int i=0;i<imax;i++) laenge[i]/=laengemax;
   }


   public void get_werte_aus_zufall()
   {              
      //**** Ermittlung der Daten mit Zufallsgenerator ****
      for(int i=0; i<imax; i++)
      {           
        //**** imax Äste mit Winkel und Länge *************
        winkel[i]=270*Math.random()-135;
        laenge[i]=3*Math.random()+1;
      }
      werte_normieren();                    
   }

   public void get_werte_aus_eingabe()
   {  
      //**** Ermittlung der Daten aus der Eingabe mit Maus *************
      for(int i=0; i<imax; i++)
      {
        double dx=(double)(200-x_eingabe[i]);
        double dy=(double)(y_eingabe[i]-250);
        winkel[i]=Math.atan2(dy,dx)*180/Math.PI+90;
        laenge[i]=Math.sqrt(dx*dx+dy*dy);
      }
      werte_normieren();
   }

   public void clrscr()
   {          
      //*** Bildschirm löschen ****************************
      g=getGraphics();
      Rectangle r=getBounds();
      g.setColor(new Color(70,80,120));
      g.fillRect(r.x,r.y,r.width,r.height);
      g.setColor(new Color(10,10,60));
      g.fillRect(r.x+5,r.y+5,r.width-10,r.height-10);
      g.setColor(Color.white);
      g.setFont(f);
   }

   public void zeichne_gabel(double x,double y,
                             double groesse,double richtung,int zaehler)
   {          
      //*** Die eigentliche Zeichenfunktion **************
      double x1,y1;   //*** Punkt der Astgabel ***********
      double gr,ri;   //*** Größe, Richtung und Zähler müssen in lokale **
      int za;         //*** Variable übertragen werden, denn die über- **
      za=zaehler+1;   //*** gebenen Variablen sind Referenzen ****
      x1=x; y1=y;
      gr=groesse;
      ri=richtung;
      if(test && (zaehler==5))
      {   
         //*** in der Testphase beim 5. Aufruf : ************
         zeitende=System.currentTimeMillis() ;
         if(zeitstart+1000<zeitende)
         {    
            //*** läuft schon über eine Sekunde, deshalb: ******
            zeitstart=zeitende;
            gr_min*=2;  //*** vergrößern von gr_min ****************
         }
      }
      for(int i=0; i<imax; i++)
      {               
         //*** jeder Ast: *****************
         double dl=gr*laenge[i];   //*** neue Länge ******************
         double w=ri+winkel[i]; //*** neuer Winkel *****************
         double w_rad=w*Math.PI/180;  //*** ins Bogenmaß umwandeln
         double x2=x1+Math.cos(w_rad)*dl;  //*** Endpunkt des Asts **
         double y2=y1-Math.sin(w_rad)*dl;  //*** berechnen **********
         if(test)
         {              
            //*** in der Testphase die max *****
            if(x2<xo) xo=x2;    //*** Ausdehnung ermitteln *********
            if(x2>xu) xu=x2;    //*** xo,yo Punkt links oben *******
            if(y2<yo) yo=y2;   //*** xu,yu Punkt rechts unten *****
            if(y2>yu) yu=y2;
        }
        else
        {   
           //*** Strecke färben ****************
           if (farbset==0)
           {
             if(gr>3*gr_min) g.setColor(new Color(200,200,250));
               else
               if (gr>gr_min) g.setColor(Color.red);
                 else g.setColor(Color.yellow);
           } else if (farbset==1)
           {
             if(gr>3*gr_min) g.setColor(Color.green);
               else
               if (gr>gr_min) g.setColor(Color.orange);
                 else g.setColor(Color.white);
           } else if (farbset==2)
           {
             if(gr>3*gr_min) g.setColor(Color.red);
               else
               if (gr>gr_min) g.setColor(Color.orange);
                 else g.setColor(Color.white);
           } else if (farbset==3)
           {
             if(gr>3*gr_min) g.setColor(Color.pink);
               else
               if (gr>gr_min) g.setColor(new Color(200,200,250));
                 else g.setColor(Color.white);
           } else if (farbset==4)
           {
             if(gr>3*gr_min) g.setColor(Color.white);
               else
               if (gr>gr_min) g.setColor(Color.magenta);
                 else g.setColor(new Color(200,200,250));
           } else if (farbset==5)
           {
             if(gr>3*gr_min) g.setColor(Color.yellow);
               else
               if (gr>gr_min) g.setColor(Color.orange);
                 else g.setColor(Color.red);
           } else if (farbset==6)
           {
             if(gr>3*gr_min) g.setColor(Color.lightGray);
               else
               if (gr>gr_min) g.setColor(Color.gray);
                 else g.setColor(Color.red);
           } else if (farbset==7)
           {
             if(gr>3*gr_min) g.setColor(Color.cyan);
               else
               if (gr>gr_min) g.setColor(Color.magenta);
                 else g.setColor(Color.yellow);
           } 
                 
           //*** endlich zeichnen!!!! *************************
            g.drawLine(trafo_x(x1),trafo_y(y1),trafo_x(x2),trafo_y(y2));
        }     
        //*** erneuter Aufruf!! ****************************
        if(gr>gr_min) zeichne_gabel(x2,y2,dl*0.7,w,za);
      }     
   }

   public void paint(Graphics g)
   {
     if(firstrun)
     {
       firstrun=false;
       zeichne_fraktal(true);
     } else
       if(eingabe)
       {
         for(int i=0;i<=i_eingabe;i++)
         {
           g.setColor(Color.white);
           g.drawLine(200,250,x_eingabe[i],y_eingabe[i]);
           if(i<i_eingabe)
           {
             g.setColor(new Color(200,200,250));
             g.fillOval(x_eingabe[i]-2,y_eingabe[i]-2,5,5);
           }
         }
         g.setColor(Color.red);
         g.fillOval(198,248,5,5);
       } else zeichne_fraktal(false);
     
     // wenn der else-Zweig entfällt, fügt man dafür
     // den Button "draw again" in init wieder ein.    
   }

   public void zeichne_fraktal(boolean newvals)
   {      
      //*** initiiert das Fraktal ************************
      if(w.isShowing()) w.setVisible(false);   //*** Grundfigur weg!! *************
      clrscr();
      if(newvals)
      {
        if(eingabe) get_werte_aus_eingabe();
          else get_werte_aus_zufall();
      }    
      x0=200; dxp=20;   //*** Irgendwelche Startwerte für **
      y0=200; dyp=20;   //*** Transformation und
      gr_min=0.015;     //*** min. Astlänge
      xu=xo=yu=yo=0;    //*** Benötiges Rechteck
      test=true;
      zeitstart=System.currentTimeMillis();
      g.drawString("Testing... please wait!",50,100);
      zeichne_gabel(0,0,1,90,0);  //*** Testaufruf ***********
      if((xu>xo)&&(yu>yo))
      {   
         //*** Falls Rechteck vernünftig ********************
         dxp=350/(xu-xo);  //*** Trafo Parameter **************
         dyp=340/(yu-yo);
         x0=-xo*dxp+20;
         y0=-yu*dyp+390;
         if(dxp>dyp) dxp=dyp; //*** dxp=dyp einheitlich damit es
         test=false;      //*** keine Verzerrungen gibt ******
         clrscr();
         zeichne_gabel(0,0,1,90,0); //*** endgültiges Zeichnen *
      }
      else g.drawString("Push button again!",20,100);
   }


   public void init( )
   {      
      //*** Oberfläche aufbauen und initiieren ***********
      setBackground(Color.black);
      addMouseListener(this);       //Ereigniswächter für      
      addMouseMotionListener(this); //Mausereignisse
      npan=new Panel();
      npan.setBackground(new Color(70,80,120));
      add(npan);
      cl=new Button("random");
      cl.setBackground(buttonC);
      npan.add(cl);
      cl.addActionListener(ew);
      cm=new Button("mouse");
      cm.setBackground(buttonC);
      npan.add(cm);
      cm.addActionListener(ew);
      cf=new Button("colorset");
      cf.setBackground(buttonC);
      npan.add(cf);
      cf.addActionListener(ew);
      cw=new Button("draw again");
      cw.setBackground(buttonC);
      //npan.add(cw); 
      // Siehe Zusammenhang mit paint
      cw.addActionListener(ew);
      fw=new Choice();  //*** Element Choice *******************
      fw.addItem("2 branches");
      fw.addItem("3 branches");
      fw.addItem("4 branches");
      fw.addItemListener(ew);
      chPan.add(fw);
      chPan.setBackground(new Color(120,120,120));
      chPan.setForeground(Color.white);
      fw.setBackground(Color.white);
      fw.setForeground(Color.black);
      npan.add(chPan);
      cr=new Button("base figure");
      cr.setBackground(buttonC);
      npan.add(cr);
      cr.addActionListener(ew);
      w=new wi();          //*** Fenster zur Darstellung der ******
      w.setSize(200,300); //*** Grundfigur ***********************
      w.setLocation(270,150);
      firstrun=true;
   }

   //*** MouseListener- und MouseMotionListener-Implementation erfordert
   //*** die folgenden Methoden ***
   
   public void mouseClicked(MouseEvent m) {}
   public void mouseEntered(MouseEvent m) {}
   public void mouseExited(MouseEvent m) {}
   public void mouseMoved(MouseEvent m) {}
     
   public void mousePressed(MouseEvent m)
   {     
      //**** Nur wenn Grundfigur mit Maus eingegeben wird ****
     if(eingabe)
     {
       if(i_eingabe<imax)
       {
         //**** Neuer Ast *****************
         x_eingabe[i_eingabe]=m.getX();
         y_eingabe[i_eingabe]=m.getY();
         paint(getGraphics());
       }
     }
   }
     
   public void mouseDragged(MouseEvent m)
   {
      //**** Nur wenn Grundfigur mit Maus eingegeben wird ***
      if(eingabe)
      {
        Graphics g=getGraphics();
        g.setXORMode(Color.black);     //*** Letzten Ast löschen und ********
        g.setColor(Color.white);
        g.drawLine(200,250,x_eingabe[i_eingabe],y_eingabe[i_eingabe]);
        x_eingabe[i_eingabe]=m.getX(); //*** neuen Wert speichern ***********
        y_eingabe[i_eingabe]=m.getY();
        g.setPaintMode();
        paint(g);
      }
   }
     
   public void mouseReleased(MouseEvent m)
   {
      //**** Nur wenn Grundfigur mit Maus eingegeben wird ***
      if(eingabe)
      {
        i_eingabe++;
        x_eingabe[i_eingabe]=200; //*** Initiieren wegen paint() ***
        y_eingabe[i_eingabe]=250;
        if(imax==i_eingabe)
        {
          zeichne_fraktal(true);
          eingabe=false;
        }
          else paint(getGraphics());          
      }
   }  

   public class Waechter implements ActionListener,ItemListener
   {
     
     public void itemStateChanged(ItemEvent e)
     {
       if (e.getSource()==fw) imax=fw.getSelectedIndex()+2;
       eingabe=false;
       zeichne_fraktal(true);
     }
     
     public void actionPerformed(ActionEvent e)
     {
       if(e.getSource()==cl)
       {    
         //**** Neues Fraktal zufällig erzeugen ******************
         eingabe=false;
         zeichne_fraktal(true);
       }
       else
       if(e.getSource()==cw)
       {    
         //**** Fraktal wiederholen ******************
         eingabe=false;
         zeichne_fraktal(false);
       }
       else       
       if(e.getSource()==cm)
       {    
         //**** Mauseingabe einleiten ******************
         eingabe=true;
         i_eingabe=0;
         x_eingabe[i_eingabe]=200;
         y_eingabe[i_eingabe]=250;
         clrscr();
         paint(getGraphics());
       }
       else
       if(e.getSource()==cf)
       {    
         //**** Fraktal wiederholen ******************
         farbset++;
         if (farbset>7) farbset=0;
         zeichne_fraktal(false);
       }
       else 
       if(e.getSource()==cr)
       {    
         //**** Grundfigur darstellen *******************
         if(w.isShowing())
         {
           w.setVisible(false);
           zeichne_fraktal(false);
         }
         else w.setVisible(true);
       }
       
     }
   }


   
   //************** Unterklasse wi zur Darstellung der Grundfigur *****

   public class wi extends Frame
   {
      Button b;
      FlowLayout fl;

      wi( )
      {
        super("base figure");
        fl=new FlowLayout();
        setLayout(fl);
        setBackground(Color.black);
        b=new Button("ok");
        b.setBackground(buttonC);
        add(b);
        b.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            setVisible(false);    
          }
        });

        addWindowListener(new wkl());
        //*** Methode von Klasse Component für Windows-Events ***
      }

      public void paint(Graphics g)
      {
        double x1,y1,w_rad;
        int x,y,xp,yp;
        xu=xo=yu=yo=0;
        for(int i=0; i<imax; i++)
        {   
          //**** Berechnen der Grundfigur und Ermitteln von
          w_rad=(90.0+winkel[i])*Math.PI/180;
          x1=Math.cos(w_rad)*laenge[i];
          y1=-Math.sin(w_rad)*laenge[i];
          if(x1<xo) xo=x1;  //*** minimalem  x ****
          if(x1>xu) xu=x1;  //*** maximalem  x ****
          if(y1<yo) yo=y1;  //*** minimalem  y ****
          if(y1>yu) yu=y1;  //*** maximalem  y ****
        }
        if((xu>xo)&&(yu>yo))
        {       
          //*** Berechnung der Transformations-Parameter **
          dxp=150/(xu-xo);
          dyp=150/(yu-yo);
          x0=-xo*dxp+30;
          y0=-yu*dyp+250;
          if(dxp>dyp) dxp=dyp;
          x=trafo_x(0);
          y=trafo_y(0);
          for(int i=0; i<imax; i++)
          {
            w_rad=(90.0+winkel[i])*Math.PI/180;
            x1=Math.cos(w_rad)*laenge[i];
            y1=-Math.sin(w_rad)*laenge[i];
            xp=trafo_x(x1);
            yp=trafo_y(y1);
            g.setColor(Color.white);
            g.drawLine(xp,yp,x,y);  //**** Strecke **********
            g.setColor(new Color(200,200,250));
            g.fillOval(xp-3,yp-3,6,6);  //**** blauer Kreis *****
          }
          g.setColor(Color.red);  //**** roter Kreis am Fußpunkt **
          g.fillOval(x-3,y-3,6,6);
        }
      } // Ende paint

      class wkl extends WindowAdapter
      {       
        //*** Die Klasse WindowAdapter enthält nur Leermethoden
        //*** Die Methoden in Gebrauch werden nun neu
        //*** überschrieben ********************************
      
        public void windowClosing(WindowEvent ke)
        {   
          //*** Nur für das Kreuzchen rechts oben!
          setVisible(false);
        } 
      }
      

   } // Ende public class wi

} // Ende Applet-Class



//*** Das Applet benötigt unbedingt mindestens die Abmessungen ***
//*** <APPLET CODE="astfrak.class" WIDTH=400 HEIGHT=400>  </APPLET>

