/* Tony Ruso and Valeriu Sitaru Fall '97 308-507A web Project
 *
 *
 */


import java.util.*;
import java.awt.*;
import java.applet.Applet;
import java.applet.AudioClip;



/****************************************************************************/
class pointExt extends Point 
{

   public pointExt(int x, int y) 
    {
      super(x, y);
    }

   /** 
    * Draw a point.
    */
   public void draw(Graphics g, int size) 
   {
      g.fillOval(x - 4, y - 4, size, size);
      g.setColor(Color.white);
      g.fillOval(x - 2, y - 2, 2, 2);
   }

   /** 
    * Draw the point being compared in different color and size.
    */
   public void blink(Graphics g) 
   {
      g.setColor(Color.red);
      g.fillOval(x - 5, y - 5, 10, 10);
      g.setColor(Color.white);
      g.fillOval(x - 3, y - 3, 2, 2);
   }
}
/***************************************************************************/
class Line 
{
   pointExt point1;
   pointExt point2;
   float    slope;
   boolean  slopeUndefine;

   /**
    * Line constructor.
    */
  /*******************************************************************/
   public Line(pointExt p1, pointExt p2) 
   {
      point1 = p1;
      point2 = p2;
      if (p1.x == p2.x)
         slopeUndefine = true;
      else 
      {    
         if (p2.y == p1.y)
            slope = (float)0;
         else
            slope = (float) (p2.y - p1.y) / (p2.x - p1.x);
         slopeUndefine = false;
     }
    }
   /*******************************************************************/ 
  

   /*******************************************************************
    * Given a Check point and determine if this check point is lying on the
    * left side or right side of the first point of the line.
    */
   public boolean onLeft(pointExt chkpt) 
    {
      if (this.slopeUndefine) 
       {
         if (chkpt.x < point1.x) return true;
         else 
          {
            if (chkpt.x == point1.x) 
             {
               if (((chkpt.y > point1.y) && (chkpt.y < point2.y)) ||
                   ((chkpt.y > point2.y) && (chkpt.y < point1.y)))
                  return true;
               else
                  return false;
             }
            else return false;
          }
        }
      else 
     {            
         /* multiply the result to avoid the rounding error */
         int x3 = (int) (((chkpt.x + slope * (slope * point1.x 
                          - point1.y + chkpt.y)) /
                         (1 + slope * slope)) * 10000);
         int y3 = (int) ((slope * (x3 / 10000 - point1.x) + point1.y) * 10000);

         if (slope == (float)0) 
        {
            if ((chkpt.y*10000) > y3) return true; else return false; 
        }
         else 
         { if (slope > (float)0) 
           {
                   if (x3 > (chkpt.x * 10000)) return true; else return false; 
            }
                else 
              {
                   if ((chkpt.x * 10000) > x3) return true; else return false; 
              }
          }
      }
  } 
   /************************************************************************/


 
    /* Draw a line.
    */
   public void draw(Graphics g) 
     {
      g.drawLine(point1.x, point1.y, point2.x, point2.y);
     }
}/**END CLASS**/

/*****************************************************************************/  


/**
 * Main Screen Panel for showing the result.
 */
/*****************************************************************************/
class SrcPanel extends Panel implements Runnable {
   ConvexHull Hull;
   Thread     kicker;

   /**
    * Variable indicates we are calculating the hull
    */
   boolean runMode = false;

   /** 
    * Variable indicates which algorithm we are using
    */
   public static final int MENU  = 0;
   public static final int Convex_Hull = 1;
   public static final int RADIAL = 2;
   public static final int Paysan = 3;
   int    algor = Convex_Hull;
   int    preAlgor;

   /**
    * Variable indicates the demonstration speed
    */
   public static final int ZERO = 0;
   public static final int FAST = 20;
   public static final int SLOW = 100;
   int    speed = SLOW;

   
   /**
    * Variable indicates the sound is on or off
    */
   boolean soundOn = true;

   /**
    * Stores all the points
    */
   Vector points = new Vector();

   /**
    * Stores all the lines in the Hull
    */
   Vector hull   = new Vector();

   /****************************************/
   Vector poly = new Vector();    /*Vector to hold polygon lines*/
   /****************************************/
   
   /**
    * Stores all the lines being checking 
    */
   Vector chkLns = new Vector();
   Vector tempLns = new Vector();
   
    /**
    * The point we are comparing with the chkLn
    */
   pointExt currPt     = new pointExt(0,0);
   int cx, cy, cz;

   public SrcPanel(ConvexHull Hull) {
      this.Hull = Hull;
   
    Hull.getone.play(); /**********ADDED LINE************/

   }

    
   /**
    * Detect the mouse down action and add the point
    */
 /*******************************************************************/  
  public synchronized boolean mouseDown(Event evt, int x, int y) 
    {
      
      if (!runMode) 
       {
         hull.removeAllElements();
         poly.removeAllElements();
         points.addElement(new pointExt(x, y));
         if (soundOn)
         Hull.clank.play();
         repaint();
        } 
     else 
     {
    stop();
    hull.removeAllElements();
    poly.removeAllElements();
         points.addElement(new pointExt(x,y));
         repaint();
         start();
      }

      return true;
   }
  /******************************************************************/
   Image offscreen;
   Dimension offscreensize;
   Graphics offgraphics;

   public void paint(Graphics g) 
   {
      Dimension d = size();
      g.setColor(Color.black);
      g.fillRect(0, 0, d.width, d.height);
    }

   /**
    * Display all the points and line in the hull.
    * If we are in execution mode, also display all the lines and points we are
    * checking
    */
   public synchronized void update(Graphics g) 
   {
      Dimension d = size();
      if ((offscreen == null) || (d.width != offscreensize.width) ||
          (d.height != offscreensize.height)) 
     {
         offscreen = createImage(d.width, d.height);
         offscreensize = d;
         offgraphics = offscreen.getGraphics();
      }

      offgraphics.setColor(Color.black);
      offgraphics.fillRect(0, 0, d.width-1, d.height-1);
 
      int np = points.size();
      int nl = hull.size();
      int polysize = poly.size(); /***Size of poly*/
      
      
     
      
      for (int i = 0; i < np; i++) 
      {
         Color ptcolor = (algor == MENU)? Color.lightGray : Color.blue;
         offgraphics.setColor(ptcolor);
         ((pointExt) points.elementAt(i)).draw(offgraphics, 8);
      };
      
      for (int a = 0; a < polysize; a++)
      {
         Color lncolor = (algor == MENU)? Color.lightGray : Color.blue;
         offgraphics.setColor(lncolor);
         ((Line) poly.elementAt(a)).draw(offgraphics);
      }


      for (int j = 0; j < nl; j++) 
      {
         Color lncolor = (algor == MENU)? Color.lightGray : Color.blue;
         offgraphics.setColor(lncolor);
         ((Line) hull.elementAt(j)).draw(offgraphics);
      }
      
      if (runMode) 
       {
         currPt.blink(offgraphics);
         offgraphics.setColor(Color.red);
    for (int k = 0; k < chkLns.size(); k++) 
        {
       ((Line)chkLns.elementAt(k)).draw(offgraphics);
         }  
    
         offgraphics.setColor(Color.gray);
    if (soundOn)
       Hull.ding.play();
        }

      /* display a menu page */

      if (algor == MENU)
       {
    if (soundOn)
       Hull.menuMusic.play();
         offgraphics.setColor(Color.green);
         
         Font font0 = new Font("Helvetica", Font.BOLD, 28);
         offgraphics.setFont(font0);
         offgraphics.drawString("Polygonization of Point Sets", 50, 50);
         Font font1 = new Font("Helvetica", Font.ITALIC, 12);
         offgraphics.setFont(font1);
         offgraphics.drawString("What you are seeing.", 50, 80);

         offgraphics.setFont(getFont());
         offgraphics.setColor(Color.blue);
         offgraphics.fillOval(44, 90, 8, 8);
         offgraphics.setColor(Color.white);
         offgraphics.fillOval(46, 92, 2, 2);
         offgraphics.setColor(Color.green);
    offgraphics.drawString("These are the points you entered", 60, 100);
        
         offgraphics.setColor(Color.red);
         offgraphics.fillOval(45, 104, 10, 10);
         offgraphics.setColor(Color.white);
         offgraphics.fillOval(46, 106, 2, 2);
         offgraphics.setColor(Color.green);
         offgraphics.drawString("These are points the algorithm is checking", 60, 115);
 
         offgraphics.setColor(Color.blue);
         offgraphics.drawLine(30, 125, 52, 125);
         offgraphics.setColor(Color.green);
         offgraphics.drawString("These are the lines of the polygon", 60, 130);

         offgraphics.setColor(Color.red);
         offgraphics.drawLine(30, 145, 52, 145);
         offgraphics.setColor(Color.green);
         offgraphics.drawString("These are temporary lines being checked by the algorithm", 60, 150);

         offgraphics.setColor(Color.green);
         offgraphics.drawString("Programmed by :Tony Ruso and Valeriu Sitaru", 50, 180);  
         offgraphics.setColor(Color.green);
         offgraphics.drawString("Click on 'Clear' to return", 50, 195);  
         if(soundOn)
          offgraphics.drawString("Sound is currently turned ON", 50, 220);
         if(!soundOn)
          offgraphics.drawString("Sound is currently turned OFF", 50, 220);  

         setMethod(preAlgor);
       }

      g.drawImage(offscreen, 0, 0, null);
   } /** END PUBLIC **/

 /************************************************************************/
   /**
    * Clear all the points and lines.
    */
   public void clearPoint() 
   {
      chkLns.removeAllElements();
      hull.removeAllElements();
      points.removeAllElements();
      poly.removeAllElements();
      repaint();
    }
 /***********************************************************************/
   /**
    * Set up which algorithm to use.  
    */
   public void setMethod(int method) 
    {
      switch (method) 
      {
         case 0:
            algor = MENU;
            break;
         case 1:
            algor = Convex_Hull;
            break;
         case 2:
            algor = RADIAL;
            break;
         case 3:
            algor = Paysan;
            break;
         default:
            algor = Convex_Hull;
            break;
       }
    }

  /***********************************************************************/
   /**
    * Run method
    */
   public void run() 
    {
      repaint();
      while (true) 
       {
         if (runMode) 
          {
            switch (algor) 
             {
               case Convex_Hull: hull.removeAllElements();
                           Convex_Hull();
                           runMode = false;
            repaint();
                           break;
               case RADIAL: hull.removeAllElements();
                           radial();
                           runMode = false;
            repaint();
                           break;
               case Paysan: hull.removeAllElements();
                            poly.removeAllElements();
                            Paysan();
                            runMode = false;
                            repaint();
                            break;            
               case MENU:  runMode = false;
                           repaint();
                           break;
               default:    System.out.println("Error in call algor\n");
              }
         
           }

         try { Thread.sleep(100); } catch (InterruptedException e) { break; }
       }
    }
 /**********************************************************************/

   public void start() 
   {
      kicker = new Thread(this);
      kicker.setPriority(Thread.MAX_PRIORITY);
      kicker.start();
   }
 /**********************************************************************/

   public void stop() 
   {
      kicker.stop();
   }
  /*********************************************************************/

   /**
    * Brute Force Algorithm implementation
    */
   public void Convex_Hull() 
    {
 
      Vector P1 = new Vector();
      Vector P2 = new Vector();
      pointExt l = (pointExt)points.elementAt(0);
      pointExt r = (pointExt)points.elementAt(0);
      int minX = l.x;
      int maxX = l.x;
      int minAt = 0;
      int maxAt = 0; 

      chkLns.removeAllElements();
      tempLns.removeAllElements();
      tempHull.removeAllElements();
      
      /* find the max and min x-coord point */

      for (int i = 1; i < points.size(); i++) {
         currPt = (pointExt) points.elementAt(i);  
         if (((pointExt)points.elementAt(i)).x > maxX) {
            r = (pointExt)points.elementAt(i);
            maxX = ((pointExt)points.elementAt(i)).x;
       maxAt = i;
         };

         if (((pointExt)points.elementAt(i)).x < minX) {
            l = (pointExt)points.elementAt(i);
            minX = ((pointExt)points.elementAt(i)).x;
       minAt = i;
         };
    repaint();
    try { Thread.sleep(speed); } catch (InterruptedException e) {}

      }

      Line lr = new Line((pointExt) l, (pointExt) r);
      tempLns.addElement(new Line((pointExt) points.elementAt(maxAt),
                          (pointExt) points.elementAt(minAt)));
      chkLns.addElement(new Line((pointExt) points.elementAt(maxAt),
                          (pointExt) points.elementAt(minAt)));
      repaint();
      try { Thread.sleep(speed); } catch (InterruptedException e) {};

      /* find out each point is over or under the line formed by the two points */
      /* with min and max x-coord, and put them in 2 group according to whether */
      /* they are above or under                                                */
      for (int i = 0; i < points.size(); i++) {
    if ((i != maxAt) && (i != minAt)) {
            currPt = (pointExt) points.elementAt(i);

            if (lr.onLeft((pointExt)points.elementAt(i))) {
               P1.addElement(new pointExt(((pointExt)points.elementAt(i)).x,
                                          ((pointExt)points.elementAt(i)).y));
            } else {
               P2.addElement(new pointExt(((pointExt)points.elementAt(i)).x,
                                       ((pointExt)points.elementAt(i)).y));
            }
            repaint();
            try { Thread.sleep(speed); } catch (InterruptedException e) {}
         }
   
      };
         
      /* put the max and min x-cord points in each group */
      P1.addElement(new pointExt(((pointExt)l).x, ((pointExt)l).y));
      P1.addElement(new pointExt(((pointExt)r).x, ((pointExt)r).y));

      P2.addElement(new pointExt(((pointExt)l).x, ((pointExt)l).y));
      P2.addElement(new pointExt(((pointExt)r).x, ((pointExt)r).y));

      /* calculate the upper hull */
      quick(P1, l, r, 0);
      
      /* display the how the upper hull was calculated */
      for (int i=0; i<tempLns.size(); i++) {
        chkLns.addElement(new Line((pointExt) ((Line)tempLns.elementAt(i)).point1, 
                                 (pointExt) ((Line)tempLns.elementAt(i)).point2));
        repaint();
        try { Thread.sleep(speed); } catch (InterruptedException e) {break;};

   for (int j=0; j<points.size(); j++) {
          if (((Line)tempLns.elementAt(i)).onLeft((pointExt)points.elementAt(j))) { 
               currPt = (pointExt) points.elementAt(j);
          repaint();
               try { Thread.sleep(speed); } catch (InterruptedException e) {break;};
          }
        }
      }


       Vector hullpts = new Vector();
       pointExt hulltemp1 = new pointExt(0,0);
       pointExt hulltemp2 = new pointExt(0,0); 
      /* put the upper hull result in final result */
      for (int k=0; k<tempHull.size(); k++)
       {
         hull.addElement(new Line((pointExt) ((Line)tempHull.elementAt(k)).point1,
                                  (pointExt) ((Line)tempHull.elementAt(k)).point2));
       
       hulltemp1 = (pointExt)((Line)tempHull.elementAt(k)).point1;
       hulltemp2 = (pointExt)((Line)tempHull.elementAt(k)).point2;
       
        for (int i=0; i < P1.size(); i++)
         {
          if ( (hulltemp1.x == ((pointExt)P1.elementAt(i)).x) &&
               (hulltemp1.y == ((pointExt)P1.elementAt(i)).y) )
               
                  P1.removeElementAt(i);
                
          else{}
          if ( (hulltemp2.x == ((pointExt)P1.elementAt(i)).x) &&
               (hulltemp2.y == ((pointExt)P1.elementAt(i)).y) )
                  P1.removeElementAt(i);
          else{}         
         }
       
        }    

     /*Now we should have the list of points in P1 that are NOT on the 
       HULL*/
     /*Now we form the main list with all points not on the Hull*/
   
      pointExt tem = new pointExt(0,0);
      
      for(int a=0; a< P1.size(); a++)  
      {
        tem = ((pointExt)P1.elementAt(a));
        P2.addElement(new pointExt( ((pointExt)tem).x, ((pointExt)tem).y) );
      
          
      }

     /*Now we sort P2 according to x*/
     
   int n= P2.size();
   int incr = n/2;
   pointExt index = new pointExt(0,0);
   while (incr >= 1)
   {
    for (int i = incr; i < n; i++)
    {
     int temp = ((pointExt)P2.elementAt(i)).x;
     index = (pointExt)P2.elementAt(i);
     int j = i;
     while (j >= incr && temp < ((pointExt)P2.elementAt(j-incr)).x)
     {
      P2.setElementAt(P2.elementAt(j-incr), j);
       j -=incr;
     }
     P2.setElementAt(index, j);
    }
    incr /= 2;
  }
  
 

/*We join lines forming the lower half of polygon */
  
   pointExt tempPt3 = new pointExt(0,0);  /*Stores the 1st point of a line in polygon*/
   pointExt tempPt4 = new pointExt(0,0);  /*Stores the 2nd point of a line in polygon*/
  
     
  for (int z1=0; z1< P2.size()-1; z1++)
    { 
     tempPt3 = (pointExt)P2.elementAt(z1);
     tempPt4 = (pointExt)P2.elementAt(z1+1); 
     
     poly.addElement(new Line( (pointExt)tempPt3,(pointExt)tempPt4) );
     repaint();   
     }    


     



      chkLns.removeAllElements();
      tempLns.removeAllElements();
      
      /* calculate the lower hull */

      chkLns.removeAllElements();
      if (soundOn)
    Hull.doneMusic.play();
   }




/*************************************************************************/
     public void Paysan()
     {
      
      Vector leftover = new Vector();
      Vector P1 = new Vector();
      Vector P2 = new Vector();
      pointExt l = (pointExt)points.elementAt(0);
      pointExt r = (pointExt)points.elementAt(0);
      int minX = l.x;
      int maxX = l.x;
      int minAt = 0;
      int maxAt = 0; 
      int testx;
      int testy;
      int htestx;
      int htesty;
      chkLns.removeAllElements();
      tempLns.removeAllElements();
      tempHull.removeAllElements();
      
      /* find the max and min x-coord point */

      for (int i = 1; i < points.size(); i++) 
      {
         currPt = (pointExt) points.elementAt(i);  
         if (((pointExt)points.elementAt(i)).x > maxX) 
         {
            r = (pointExt)points.elementAt(i);
            maxX = ((pointExt)points.elementAt(i)).x;
       maxAt = i;
         };

         if (((pointExt)points.elementAt(i)).x < minX)
          {
            l = (pointExt)points.elementAt(i);
            minX = ((pointExt)points.elementAt(i)).x;
       minAt = i;
         };
    repaint();
    try { Thread.sleep(speed); } catch (InterruptedException e) {}

      } /*END FOR*/

      Line lr = new Line((pointExt) l, (pointExt) r);
      tempLns.addElement(new Line((pointExt) points.elementAt(maxAt),
                          (pointExt) points.elementAt(minAt)));
      chkLns.addElement(new Line((pointExt) points.elementAt(maxAt),
                          (pointExt) points.elementAt(minAt)));
      repaint();
      try { Thread.sleep(speed); } catch (InterruptedException e) {};

      /* find out each point is over or under the line formed by the two points */
      /* with min and max x-coord, and put them in 2 group according to whether */
      /* they are above or under                                                */

      for (int i = 0; i < points.size(); i++) 
       {
    if ((i != maxAt) && (i != minAt)) 
     {
            currPt = (pointExt) points.elementAt(i);

            if (lr.onLeft((pointExt)points.elementAt(i))) 
             {
               P1.addElement(new pointExt(((pointExt)points.elementAt(i)).x,
                                          ((pointExt)points.elementAt(i)).y));
            } else
             {
               P2.addElement(new pointExt(((pointExt)points.elementAt(i)).x,
                                       ((pointExt)points.elementAt(i)).y));
            }
            repaint();
            try { Thread.sleep(speed); } catch (InterruptedException e) {}
         }/*END FOR*/
   
      };
   /* P2 is the lower hull */
   P2.addElement(new pointExt(((pointExt)l).x, ((pointExt)l).y));
      P2.addElement(new pointExt(((pointExt)r).x, ((pointExt)r).y));
 
  P1.addElement(new pointExt(((pointExt)l).x, ((pointExt)l).y));
      P1.addElement(new pointExt(((pointExt)r).x, ((pointExt)r).y));

    /*******************************************************************
                     Sort Points
                     */

/** We sort the upper half of the points (P2 points)***************/

   int n= P2.size();
   int incr = n/2;
   pointExt index = new pointExt(0,0);
   while (incr >= 1)
   {
    for (int i = incr; i < n; i++)
    {
     int temp = ((pointExt)P2.elementAt(i)).x;
     index = (pointExt)P2.elementAt(i);
     int j = i;
     while (j >= incr && temp < ((pointExt)P2.elementAt(j-incr)).x)
     {
      P2.setElementAt(P2.elementAt(j-incr), j);
       j -=incr;
     }
     P2.setElementAt(index, j);
    }
    incr /= 2;
  }
  
 

/*We join lines forming the lower half of polygon */
  
   pointExt tempPt3 = new pointExt(0,0);  /*Stores the 1st point of a line in polygon*/
   pointExt tempPt4 = new pointExt(0,0);  /*Stores the 2nd point of a line in polygon*/
  
     
  for (int z1=0; z1< P2.size()-1; z1++)
    { 
     tempPt3 = (pointExt)P2.elementAt(z1);
     tempPt4 = (pointExt)P2.elementAt(z1+1); 
     
     poly.addElement(new Line( (pointExt)tempPt3,(pointExt)tempPt4) );
//     repaint();   
     }    



/** We sort the lower half of the points (P1 points)***************/

   int n1= P1.size();
   int incr1 = n1/2;
   pointExt index1 = new pointExt(0,0);
   while (incr1 >= 1)
   {
    for (int i1 = incr1; i1 < n1; i1++)
    {
     int temp1 = ((pointExt)P1.elementAt(i1)).x;
     index1 = (pointExt)P1.elementAt(i1);
     int j1 = i1;
     while (j1 >= incr1 && temp1 < ((pointExt)P1.elementAt(j1-incr1)).x)
     {
      P1.setElementAt(P1.elementAt(j1-incr1), j1);
       j1 -=incr1;
     }
     P1.setElementAt(index1, j1);
    }
    incr1 /= 2;
  }
  

     
   /*now we have points to form lines of the polygon */
     
    
   pointExt tempPt1 = new pointExt(0,0);  /*Stores the 1st point of a line in polygon*/
   pointExt tempPt2 = new pointExt(0,0);  /*Stores the 2nd point of a line in polygon*/
  
   int indexPt;
 
/*We join lines forming upper half of polygon*/
   
   for (int z=0; z< P1.size()-1; z++)
    { 
     tempPt1 = (pointExt)P1.elementAt(z);
     tempPt2 = (pointExt)P1.elementAt(z+1); 
     
     poly.addElement(new Line( (pointExt)tempPt1,(pointExt)tempPt2) );
     repaint();   
     }

  
  
      /* show how the lower hull was calculated */
/*      for (int i=0; i<tempLns.size(); i++) 
      {
        chkLns.addElement(new Line((pointExt) ((Line)tempLns.elementAt(i)).point1, 
                                 (pointExt) ((Line)tempLns.elementAt(i)).point2));
        repaint();
        try { Thread.sleep(speed); } catch (InterruptedException e) {break;};

   for (int j=0; j<points.size(); j++)
    {
          if (!((Line)tempLns.elementAt(i)).onLeft((pointExt)points.elementAt(j))) {   
            {   
               currPt = (pointExt) points.elementAt(j);
          P1.addElement((pointExt)points.elementAt(j-1));  
       }   
     repaint();
          try { Thread.sleep(speed); } catch (InterruptedException e) {break;};
          }
       }
 }
 
  

  for (int k=0; k<tempHull.size(); k++) 
  {
    hull.addElement(new Line((pointExt) ((Line)tempHull.elementAt(k)).point1,
                                (pointExt) ((Line)tempHull.elementAt(k)).point2));
   } 
 
 
    

   /** DONE PAYSAN **/
      if (soundOn)
    Hull.doneMusic.play();

}


/****************************************************************************/

   int indexChkLn = 0;

   /** 
    * Quick Hull Algorithm implementation.
    * Calculate the hull first and display the execution with the information from
    * chklns and tempHull.
    */

   Vector tempHull = new Vector();



public void radial() 
{
      
      Vector leftover = new Vector();
      Vector valuevector = new Vector();
      pointExt l = (pointExt)points.elementAt(0);
      pointExt r = (pointExt)points.elementAt(0);
      int minY = l.y;
      int minAt = 0;
      int maxAt = 0; 
      double deltax, deltay, hypothenuse; 
      int cosine, sine=0;
      int testx;
      int testy;
      int htestx;
      int htesty;
      chkLns.removeAllElements();
      tempLns.removeAllElements();
      tempHull.removeAllElements();
      
      /* find the min y-coord point */

      pointExt minpoint = new pointExt(0,0);
      
      int min_y= ((pointExt)points.elementAt(0)).y; 
      for (int i = 0; i < points.size(); i++) 
      {
    
         if (((pointExt)points.elementAt(i)).y < min_y)
            {
//          l = (pointExt)points.elementAt(i);
            min_y = ((pointExt)points.elementAt(i)).y;
            minAt = i;
            minpoint= (pointExt)points.elementAt(i);
            };
         repaint();
         try { Thread.sleep(speed); } catch (InterruptedException e) {}

      } /*END FOR*/

      /* partition the points into two groups according to their*/
      /* relative position to the lowest point (to the left or right)*/
      /* compute sines for the points to the left and cosines for the
      /* points to the right of the lowest point. Add an arbitrary value of
      /* 1 to the cosines of the points to the right so that their values */
      /* are always larger than the sines (0 to 1) of the points to the left
      /* of the lowest ones. Store values and indexes in sinesvector 
      /* in valuevector */

      for (int i = 0; i < points.size(); i++) 
          {
          if (i != minAt) 
            {
            // deltax represents the size of the vertical side in a right angle triangle
            deltax = ((pointExt)points.elementAt(i)).x - ((pointExt)points.elementAt(minAt)).x; 
            // deltay represents the size of the horizontal side in a right angle triangle
            deltay = ((pointExt)points.elementAt(i)).y - ((pointExt)points.elementAt(minAt)).y; 
            // according to Pythagoras, in a right angle triangle
            // c^2= a^2 + b^2, where c is the hypothenuse
            hypothenuse = Math.sqrt(deltax*deltax + deltay*deltay);
            // if the point we're evaluating is to the left of the point having the min y coord
            if (((pointExt)points.elementAt(i)).x < ((pointExt)points.elementAt(minAt)).x)
               { // we'll compute the sine = opposite side/hypothenuse
               sine = (int)(deltay/hypothenuse*10000);
               valuevector.addElement(new pointExt(sine,i));
               }
            else
               { // else, compute it's cosine and add a constant to it to insure 
                 // larger values than max(sin) = 1
               cosine = (int)(deltax/hypothenuse*10000 + 10000);
               valuevector.addElement(new pointExt(cosine,i));
               };
            }
            repaint();
            try { Thread.sleep(speed); } catch (InterruptedException e) {}
         }/*END FOR*/


      /* sort points from 0 to pi/2 according to their sine/cosine value */       

   int n1= valuevector.size();
   int incr1 = n1/2;
   pointExt index1 = new pointExt(0,0);
   while (incr1 >= 1)
   {
    for (int i1 = incr1; i1 < n1; i1++)
    {
     int temp1 = ((pointExt)valuevector.elementAt(i1)).x;
     index1 = (pointExt)valuevector.elementAt(i1);
     int j1 = i1;
     while (j1 >= incr1 && temp1 < ((pointExt)valuevector.elementAt(j1-incr1)).x)
     {
      valuevector.setElementAt(valuevector.elementAt(j1-incr1), j1);
       j1 -=incr1;
     }
     valuevector.setElementAt(index1, j1);
    }
    incr1 /= 2;
  }
  
  /*                   
      int n = valuevector.size();
      int incr = n/2;
      pointExt index = new pointExt(0,0);
      pointExt value = new pointExt(0,0);
      while (incr >= 1)
         {
         for (int i = incr; i < n; i++)
           {
           int temp = ((pointExt)valuevector.elementAt(i)).x;
           index = (pointExt)valuevector.elementAt(i);
           int j = i;
           while (j >= incr && temp < ((pointExt)valuevector.elementAt(j-incr)).x)
              {
              value = (pointExt)valuevector.elementAt(j-incr);
              valuevector.setElementAt(value, j);
              j -=incr;
              }
           valuevector.setElementAt(index, j);
           }
         incr /= 2;
         } 
    
    */     
       /*Now value vector is sorted by sine/cosine values, we must map these
       values to the actual points called actualPts*/
       
       int k = valuevector.size();
       Vector actualPts = new Vector();
       pointExt point = new pointExt(0,0);
       
       for(int a=0; a < k; a++)
       {
        int ind = ((pointExt)valuevector.elementAt(a)).y;
        point = (pointExt)points.elementAt(ind);
        actualPts.addElement(new pointExt(point.x, point.y));
        }
         
      /* draw lines from 0 to pi/2 */

      pointExt tempPt3 = new pointExt(0,0);  /*Stores the 1st point of a line in polygon*/
      pointExt tempPt4 = new pointExt(0,0);  /*Stores the 2nd point of a line in polygon*/
      pointExt last = new pointExt(0,0);
      
      pointExt tem = new pointExt(0,0);
      int ss = actualPts.size();
      tem = (pointExt)actualPts.elementAt(0);
   
     for (int z1=0; z1< actualPts.size()-1; z1++)
          { 
          tempPt3 = (pointExt)actualPts.elementAt(z1);
          tempPt4 = (pointExt)actualPts.elementAt(z1+1); 
     
          poly.addElement(new Line( (pointExt)tempPt3,(pointExt)tempPt4) );
          repaint();   
          }   
          
            last = (pointExt)actualPts.elementAt(ss-1);
         // tempPt4 = (pointExt)actualPts.elementAt(0); 
      
       pointExt minnn = new pointExt(0,0);
       minnn = (pointExt)points.elementAt(minAt);    

          poly.addElement(new Line( (pointExt)tem,(pointExt)minnn) );
          poly.addElement(new Line( (pointExt)last,(pointExt)minnn ));
   
      if (soundOn)
    Hull.doneMusic.play();

};



















       

/*******************************************************************/
   /**
    * Recursive method to find out the Hull.
    * faceDir is 0 if we are calculating the upper hull.
    * faceDir is 1 if we are calculating the lower hull.
    */ 
   public synchronized void quick(Vector P, pointExt l, pointExt r, int faceDir) {
      if (P.size() == 2) {
         tempHull.addElement(new Line((pointExt) P.elementAt(0),
                                  (pointExt) P.elementAt(1)));
         return;
      } else {
    int hAt = splitAt(P, l, r);
         Line lh = new Line((pointExt) l, (pointExt) P.elementAt(hAt));
         Line hr = new Line((pointExt) P.elementAt(hAt), (pointExt) r);
         Vector P1 = new Vector();
         Vector P2 = new Vector();

         for (int i = 0; i < (P.size() - 2); i++) {
       if (i != hAt) {
               currPt = (pointExt) P.elementAt(i);
          if (faceDir == 0) {
                  if (lh.onLeft((pointExt)P.elementAt(i))) {
                     P1.addElement(new pointExt(((pointExt)P.elementAt(i)).x,
                                                ((pointExt)P.elementAt(i)).y));
                  }

        if ((hr.onLeft((pointExt)P.elementAt(i)))) {
                  P2.addElement(new pointExt(((pointExt)P.elementAt(i)).x,
                                             ((pointExt)P.elementAt(i)).y));
                  }
          } else {
                  if (!(lh.onLeft((pointExt)P.elementAt(i)))) {
                     P1.addElement(new pointExt(((pointExt)P.elementAt(i)).x,
                                                ((pointExt)P.elementAt(i)).y));
                  };
   
             if (!(hr.onLeft((pointExt)P.elementAt(i)))) {
                  P2.addElement(new pointExt(((pointExt)P.elementAt(i)).x,
                                             ((pointExt)P.elementAt(i)).y));
                  }; 
          };
            }
         }

         P1.addElement(new pointExt(((pointExt)l).x, ((pointExt)l).y));
         P1.addElement(new pointExt(((pointExt)P.elementAt(hAt)).x, 
                ((pointExt)P.elementAt(hAt)).y));

         P2.addElement(new pointExt(((pointExt)P.elementAt(hAt)).x, 
                ((pointExt)P.elementAt(hAt)).y));
         P2.addElement(new pointExt(((pointExt)r).x, ((pointExt)r).y));
    
    pointExt h = new pointExt(((pointExt)P.elementAt(hAt)).x,
                    ((pointExt)P.elementAt(hAt)).y);

         tempLns.addElement(new Line((pointExt) l, (pointExt) h));
         tempLns.addElement(new Line((pointExt) h, (pointExt) r));

    if (faceDir == 0) {
            quick(P1, l, h, 0);
            quick(P2, h, r, 0);
    } else {
       quick(P1, l, h, 1);
            quick(P2, h, r, 1);
         }
      return;
      }
   }



 





   /**
    * Find out a point which is in the Hull for sure among a group of points
    * Since all the point are on the same side of the line formed by l and r,
    * so the point with the longest distance perpendicular to this line is 
    * the point we are lokking for.
    * Return the index of this point in the Vector/
    */
   public synchronized int splitAt(Vector P, pointExt l, pointExt r) {
      double    maxDist = 0;
      Line newLn = new Line((pointExt) l, (pointExt) r);

      int x3 = 0, y3 = 0;
      double distance = 0;
      int farPt = 0;

      for (int i = 0; i < (P.size() - 2); i++) {
         if (newLn.slopeUndefine) {
            x3 = l.x;
            y3 = ((pointExt)P.elementAt(i)).y;
         } else {
            if (r.y == l.y) {
               x3 = ((pointExt)P.elementAt(i)).x;
               y3 = l.y;
            } else {
                  x3 = (int) (((((pointExt)P.elementAt(i)).x + newLn.slope *
                                (newLn.slope * l.x - l.y +
                                ((pointExt)P.elementAt(i)).y))
                              / (1 + newLn.slope * newLn.slope)));
                  y3 = (int) ((newLn.slope * (x3 - l.x) + l.y));
            }
         }
         int x1 = ((pointExt)P.elementAt(i)).x;
         int y1 = ((pointExt)P.elementAt(i)).y;
         distance = Math.sqrt(Math.pow((y1-y3), 2) + Math.pow((x1-x3), 2));

         if (distance > maxDist) {
            maxDist = distance;
            farPt = i;
         }
      }
      return farPt;
   }
}

/**
 * Control panel for Convex Hull.
 */

public class ConvexHull extends Applet {
   SrcPanel srcPanel;
   Button   clearButton, doneButton, instruction;
   Checkbox sound;
   Choice   algorithms;
   AudioClip ding, clank, getone, doneMusic, menuMusic;
 
   public void init() {
      ding      = getAudioClip(getCodeBase(), "audio/ding.au");
      clank     = getAudioClip(getCodeBase(), "audio/clank6.au");
      getone    = getAudioClip(getCodeBase(), "audio/title.au");
      doneMusic = getAudioClip(getCodeBase(), "audio/done.au");
      menuMusic = getAudioClip(getCodeBase(), "audio/return.au");
      
     
      

      setLayout(new BorderLayout());
      srcPanel = new SrcPanel(this);
      add("Center", srcPanel);
      Panel ctrlPanel = new Panel();
      add("South", ctrlPanel);

      algorithms = new Choice();
      algorithms.addItem("Convex Bottom");
      algorithms.addItem("Radial");
      algorithms.addItem("2 Peasants");
      ctrlPanel.add(algorithms);

      Choice speed = new Choice();
      speed.addItem("Slow Demo");
      speed.addItem("Fast Demo");
      speed.addItem("No Delay");
      ctrlPanel.add(speed);
      sound = new Checkbox("Sound");
      sound.setState(true);
      ctrlPanel.add(sound);
      
      clearButton = new Button("Clear");
      ctrlPanel.add(clearButton);
      doneButton  = new Button("Polygonize");
      ctrlPanel.add(doneButton);

      instruction = new Button("Confused?");
      ctrlPanel.add(instruction);
    }

   public void start() {
      srcPanel.start();
   }

   public void stop() {
      srcPanel.stop();
   }



   
   public boolean action(Event e, Object arg) {
      if (e.target instanceof Button) {
         if ((e.target).equals(instruction)) {
            srcPanel.preAlgor = srcPanel.algor;
            srcPanel.setMethod(SrcPanel.MENU);
            srcPanel.runMode = true;
         };

         if ((e.target).equals(clearButton)) {
            if (!srcPanel.runMode) {
               srcPanel.clearPoint();
            } else {
          srcPanel.stop();
               srcPanel.runMode = false;
               srcPanel.clearPoint();
               srcPanel.start();
            }  
         };

         if ((e.target).equals(doneButton)) {
            if (srcPanel.points.size() > 2 ) 
               srcPanel.runMode = true;
         };

      };

      if (e.target instanceof Choice) {
         String choice = (String)arg;
         if (choice.equals("Convex Bottom")) 
          {
            srcPanel.setMethod(SrcPanel.Convex_Hull);
         } 
         else if (choice.equals("Radial")) 
         {
            srcPanel.setMethod(SrcPanel.RADIAL);
         } 
         else if (choice.equals("2 Peasants"))
         {
             srcPanel.setMethod(SrcPanel.Paysan);
         } 
         else if (choice.equals("Slow Demo")) {
            srcPanel.speed = SrcPanel.SLOW;
         } else if (choice.equals("Fast Demo")) {
            srcPanel.speed = SrcPanel.FAST;
    } else if (choice.equals("No Delay")) {
       srcPanel.speed = SrcPanel.ZERO;
         }
      };

      if (e.target instanceof Checkbox) {
    srcPanel.soundOn = ((Boolean)arg).booleanValue();
      };

      return true;
   }
}



