// Program : Chaotic Liar
// Author  : Apostolos Syropoulos apostolo@obelix.ee.duth.gr
// Date    : 07 Jun 1999

import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;

public class liar extends Applet implements Runnable, 
                                              ActionListener,
                                              MouseListener,
                                              MouseMotionListener
{
   
       final double minR=-1.5, maxR=2.5, minI=-1.5, maxI=2.5;
       DataField xmin, xmax, ymin, ymax;
       Button drawButton, resetButton, quitButton;
       LCanvas drawingBoard;
       Thread MThread;
       static boolean inApplet=true; // is it an applet or an application? 
       
       public void init()
       {
                 GridBagLayout gridbag = new GridBagLayout();
                 GridBagConstraints c = new GridBagConstraints();
                 setLayout(gridbag);

          
                 drawingBoard = new LCanvas(minR, maxR, minI, maxI);
                 c.insets = new Insets(4,4,5,5);
                 c.gridheight = 500;
                 c.gridwidth  = GridBagConstraints.REMAINDER;
                 c.weightx = 1.0;
                 c.weighty = 1.0;
                 c.fill = GridBagConstraints.BOTH;
                 c.anchor = GridBagConstraints.CENTER;
                 gridbag.setConstraints(drawingBoard, c);
                 drawingBoard.addMouseListener(this);
                 drawingBoard.addMouseMotionListener(this);
                 add(drawingBoard);

                 
                 xmin = new DataField("xmin", minR);
                 c.insets= new Insets(1,1,1,1);
                 c.gridx= GridBagConstraints.RELATIVE;
                 c.gridy= GridBagConstraints.RELATIVE;
                 c.gridheight = 1;
                 c.gridwidth  = GridBagConstraints.RELATIVE;
                 c.weightx = 0.0;
                 c.weighty = 0.0;
                 gridbag.setConstraints(xmin, c);
                 add(xmin);

                 xmax = new DataField("xmax", maxR);
                 c.gridwidth = GridBagConstraints.REMAINDER;
                 gridbag.setConstraints(xmax, c);
                 add(xmax);
                 
                 ymin = new DataField("ymin", minI);
                 c.gridwidth=GridBagConstraints.RELATIVE;
                 gridbag.setConstraints(ymin, c);
                 add(ymin);

                 ymax = new DataField("ymax", maxI);
                 c.gridwidth = GridBagConstraints.REMAINDER;
                 gridbag.setConstraints(ymax, c);
                 add(ymax);
       
                 setFont(new Font("Courier", Font.BOLD, 16));
                 drawButton = new Button("Draw Picture");
                 c.gridheight = 2;
                 c.fill = GridBagConstraints.NONE;
                 c.gridwidth  = GridBagConstraints.RELATIVE;
                 gridbag.setConstraints(drawButton, c);
                 drawButton.addActionListener(this);
                 add(drawButton);

                 resetButton = new Button("Draw Original Picture");
                 c.gridwidth  = GridBagConstraints.REMAINDER;
                 gridbag.setConstraints(resetButton, c);
                 resetButton.addActionListener(this);
                 add(resetButton);

                 if(!inApplet)
		 {
                    quitButton = new Button("Quit");
                    c.gridwidth  = GridBagConstraints.REMAINDER;
                    gridbag.setConstraints(quitButton, c);
                    quitButton.addActionListener(this);
                    add(quitButton);
		 }

                 MThread = new Thread(this);
                 MThread.start();
      
       }

       public void run()
       {
          drawingBoard.repaint();
       }

   
       public static void main(String[] args)
       {
          inApplet = false;
          Frame f = new Frame("Chaotic Liar Drawing Program");
          liar m = new liar();
          
          m.init();
          f.add("Center", m);
          f.pack();
          f.setVisible(true);
       }

       public void actionPerformed(ActionEvent e)
       {
          Object source = e.getSource();
          if (source == resetButton)
          { 
             drawingBoard.pmin = minR;
             drawingBoard.pmax = maxR;                  
             drawingBoard.qmin = minI;
             drawingBoard.qmax = maxI;
             xmax.setDataField(maxR);
             ymax.setDataField(maxI); 
             xmin.setDataField(minR);
             ymin.setDataField(minI);
             if (!MThread.isAlive())
             {
                MThread = new Thread(this);
                MThread.start();
             }
          }
          else if (source == drawButton && !MThread.isAlive())
          {
                   (MThread = new Thread(this)).start();
          }
          else if (source == quitButton)
	  {
              System.exit(0);
          }
       }


       public void mousePressed(MouseEvent e)
       {
          xmin.setDataField(drawingBoard.pmin);
          ymin.setDataField(drawingBoard.qmin);
       }

       public void mouseReleased(MouseEvent e)
       {
          xmax.setDataField(drawingBoard.pmax);
          ymax.setDataField(drawingBoard.qmax); 
          xmin.setDataField(drawingBoard.pmin);
          ymin.setDataField(drawingBoard.qmin);
       }

       public void mouseDragged(MouseEvent e)
       {
          xmax.setDataField(drawingBoard.pmax);
          ymax.setDataField(drawingBoard.qmax);
          xmin.setDataField(drawingBoard.pmin);
          ymin.setDataField(drawingBoard.qmin);
       }

       public void mouseClicked(MouseEvent e)
       { }

       public void mouseEntered(MouseEvent e)
       { }
     
       public void mouseExited(MouseEvent e)
       { }

       public void mouseMoved(MouseEvent e)
       { }

}

class DataField extends Panel
{
        double DataFieldValue; 
        private Label title;   
        TextField datafield;

        DataField (String LabelTitle, double initValue)
        {
            setLayout(new GridLayout(2,1,2,2));
            title = new Label(LabelTitle, Label.CENTER);
            add(title);
            datafield = new TextField(Double.toString(initValue), 25);
            datafield.setEditable(false);
            add(datafield);
            DataFieldValue = initValue;
        }

        void setDataField (double d)
        {
            datafield.setText(Double.toString(d));
            DataFieldValue = d;
        }

}

class LCanvas extends Canvas implements MouseListener, MouseMotionListener
{
    final int XScreen = 500;
    final int YScreen = 500;
    private double DeltaP, DeltaQ;      
    final int MaxIter=40;
    private double Pmin, Pmax, Qmin, Qmax;
    double pmin, pmax, qmin, qmax;
    Dimension minSize;

    public LCanvas(double pmin, double pmax, double qmin, double qmax)
    {  
        super();
        this.pmin = Pmin = pmin;
        this.pmax = Pmax = pmax;
        this.qmin = Qmin = qmin;
        this.qmax = Qmax = qmax;
        minSize = new Dimension(XScreen, YScreen);
        addMouseListener(this);
        addMouseMotionListener(this); 
    }
    
    public Dimension getPreferredSize()
    {    
       return getMinimumSize();
    }

    public synchronized Dimension getMinimumSize()
    {
       return minSize;
    }

    public void mouseClicked(MouseEvent event)
    { }
    

    public void mousePressed(MouseEvent event)
    {  
       int x=event.getX(), y=event.getY(), X, Y;
  
       X = (x > (XScreen - 1)) ? (XScreen - 1) : ((x < 0) ? 0 : x);
       Y = (y > (YScreen - 1)) ? (YScreen - 1) : ((y < 0) ? 0 : y);
       pmin = Pmin + DeltaP*X;
       qmax = Qmax - DeltaQ*Y;
    }

    public void mouseEntered(MouseEvent event)
    { }

    public void mouseExited(MouseEvent event)
    { }

    public void mouseReleased(MouseEvent event)
    {
       int x=event.getX(), y=event.getY(), X, Y;
       double temp;
   
       X = (x > (XScreen - 1)) ? (XScreen - 1) : ((x < 0) ? 0 : x);
       Y = (y > (YScreen - 1)) ? (YScreen - 1) : ((y < 0) ? 0 : y);
       pmax = Pmin + DeltaP*X;
       qmin = Qmax - DeltaQ*Y;
       if (qmin > qmax )
       {
         temp = qmin;
         qmin = qmax;
         qmax = temp;
       }
       if (pmin > pmax )
       {
         temp = pmin;
         pmin = pmax;
         pmax = temp;
       }
    }
     
    public void mouseDragged(MouseEvent event)
    {
       int x=event.getX(), y=event.getY(), X, Y;
       double temp;

       X = (x > (XScreen - 1)) ? (XScreen - 1) : ((x < 0) ? 0 : x);
       Y = (y > (YScreen - 1)) ? (YScreen - 1) : ((y < 0) ? 0 : y);
       pmax = Pmin + DeltaP*X;
       qmin = Qmax - DeltaQ*Y;
       if (qmin > qmax )
       {
         temp = qmin;
         qmin = qmax;
         qmax = temp;
       }
       if (pmin > pmax )
       {
         temp = pmin;
         pmin = pmax;
         pmax = temp;
       }       
    }   

    public void mouseMoved(MouseEvent event)
    { }

    Color colour(int a)
    {
       Color paintingColor = Color.white;
       switch (a % 12)
         {
         case 0: paintingColor = Color.blue; 
                 break; 
         case 1: paintingColor = Color.cyan;
                 break; 
         case 2: paintingColor = Color.darkGray;
                 break; 
         case 3: paintingColor = Color.gray;
                 break; 
         case 4: paintingColor = Color.green;
                 break; 
         case 5: paintingColor = Color.lightGray;
                 break; 
         case 6: paintingColor = Color.magenta;
                 break; 
         case 7: paintingColor = Color.orange;
                 break;
         case 8: paintingColor = Color.pink;
                 break;
         case 9: paintingColor = Color.red;
                 break;
        case 10: paintingColor = Color.white;
                 break;
        case 11: paintingColor = Color.yellow;
         }
     return paintingColor;
    }

    public void paint(Graphics g)
    {    
        final double bailout=1.0609;
        double x, y, xnew;
        int nx, ny, iter;        

        Pmin = pmin;
        Qmax = qmax;
        Pmax = pmax;
        Qmin = qmin;
        DeltaP=(pmax-pmin)/(XScreen - 1);
        DeltaQ=(qmax-qmin)/(YScreen - 1);
        for (nx=0; nx < (XScreen - 1); nx++)
        { 
           for(ny=0; ny < (YScreen -1); ny++)
	   {
              x=pmin+nx*DeltaP;
              y=qmin+ny*DeltaQ;
              iter=0; 
              loop: while(iter < MaxIter)
	      { 
                  xnew=1.0-Math.abs(y-x);
                  y=1.0-Math.abs((1.0-x)-y);
                  x=xnew;
                  iter++;
                  if((x*x+y*y) > bailout)
                     break loop;
              }
              g.setColor((iter == MaxIter) ? Color.black : colour(iter));
              g.drawLine(nx, ny, nx, ny);
	   }
	}
    }
}

