/* 
   Amoeba - a class which draws an amoeba.
   Nina Amenta, Sept 2001
*/

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class Amoeba
    extends ApplicationFrame 
    implements MouseListener, MouseMotionListener {

  // data 
  protected Point2D[] mPoints;          // the control points
  protected int selectedIndex;          // the one selected
  
  // Constructor
  public Amoeba() {
    super("Amoeba");    // ApplicationWindow constructor, name of window
    setSize(500, 400);
    center();           // center on screen
    
    // Set up initial positions of points
    mPoints = new Point2D[12];
    // Cubic curve - upper left
    mPoints[0] = new Point2D.Double(50, 100);
    mPoints[1] = new Point2D.Double(100, 50);
    mPoints[2] = new Point2D.Double(250, 50);
    // Cubic curve - upper right
    mPoints[3] = new Point2D.Double(400,50);
    mPoints[4] = new Point2D.Double(450,100);
    mPoints[5] = new Point2D.Double(450,200);
    // Cubic curve - lower right
    mPoints[6] = new Point2D.Double(450,300);
    mPoints[7] = new Point2D.Double(400,350);
    mPoints[8] = new Point2D.Double(250,350);
    // Cubic curve - lower left
    mPoints[9] = new Point2D.Double(100,350);
    mPoints[10] = new Point2D.Double(50,300);
    mPoints[11] = new Point2D.Double(50,200);
    

    selectedIndex = -1;

    // Listen for mouse events.
    addMouseListener(this);     // Hey AWT! Tell me about mouse clicks!
    addMouseMotionListener(this);   // And about motions!

    setVisible(true);           // Display the Window
  }

  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D)g;

    // Draw the amoeba
    GeneralPath blob = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
    // Start the path at the last point in the list
    blob.moveTo((float)mPoints[11].getX(),(float)mPoints[11].getY());
    // Sadly, the methods of GeneralPath expect float arguements.
    // Each step in the path is a curve, starting at the last point 
    // given, and defined by that point and the three passed as 
    // arguments.
    for (int i=0; i<4; i++) {
        blob.curveTo((float)mPoints[3*i].getX(),(float)mPoints[3*i].getY(),
             (float)mPoints[3*i+1].getX(),(float)mPoints[3*i+1].getY(),
             (float)mPoints[3*i+2].getX(),(float)mPoints[3*i+2].getY());
        }
    g2.setPaint(Color.yellow);
    g2.fill(blob);
    g2.setPaint(Color.black);
    g2.draw(blob);

    // The nucleus of the cell
    Ellipse2D nucleus = new 
                    Ellipse2D.Double(200,160,100,80);
    g2.setPaint(Color.pink);
    g2.fill(nucleus);
    g2.setPaint(Color.black);
    g2.draw(nucleus);

    // Draw the control polygon
    GeneralPath control = new 
            GeneralPath(GeneralPath.WIND_EVEN_ODD);
    // Each step in the path is a line segment
    control.moveTo((float)mPoints[11].getX(),(float)mPoints[11].getY());
    for (int i=0; i<12; i++) {
        control.lineTo((float)mPoints[i].getX(),(float)mPoints[i].getY());
        }
    g2.setPaint(Color.lightGray);
    g2.draw(control);


    for (int i = 0; i < mPoints.length; i++) {
      // If the control point is selected, color it red.
      if (i == selectedIndex)
        g2.setPaint(Color.red);
      else
	  {
	  if  ((i==2) || (i==5) || (i==8) || (i==11))   // endpoints
	      {
	      g2.setPaint(Color.green);
	      } 
	  else  // non-endpoints
	      {
	      g2.setPaint(Color.blue);
	      }
	  }
      // Draw the point.
      g2.fill(getControlPoint(mPoints[i]));
    }

  }


  /* Now all the mouse handling code */

  // We'll need this in a minute...
  // notice it returns something that implements the Shape interface, 
  // we don't need to say what. 
  protected Shape getControlPoint(Point2D p) {
    // Create a small square around the given point.
    int side = 4;
    return new Rectangle2D.Double(
        p.getX() - side / 2, p.getY() - side / 2,
        side, side);
  }
  

   // When the user clicks, select the point under the cursor (if there is one)
  public void mousePressed(MouseEvent me) {
    selectedIndex = -1;
    for (int i = 0; i < mPoints.length; i++) {
      Shape s = getControlPoint(mPoints[i]);
      // me.getPoint() returns the position of the cursor. 
      // s.contains returns true if that point is in Shape s,
      // which is one of the little rectangles around the 
      // control points.
      if (s.contains(me.getPoint())) {
        selectedIndex = i;
        break;
      }
    }
    repaint(); // Hey AWT! Call my paint function!
  }

    // When the user drags the mouse with the button held down, 
    // and a control point is selected, move the control point along with 
    // the cursor.
  public void mouseDragged(MouseEvent me) {
    if (selectedIndex != -1) {
      mPoints[selectedIndex].setLocation(me.getPoint());
      repaint();
    }
  }

  // methods of MouseListener and MouseMotionListener that we
  // have to implement, but shouldn't do anything in this application.
  public void mouseClicked(MouseEvent me) {}
  public void mouseReleased(MouseEvent me) {}
  public void mouseMoved(MouseEvent me) {}
  public void mouseEntered(MouseEvent me) {}
  public void mouseExited(MouseEvent me) {}

  // main method, starts up
  public static void main(String[] args) {
    new Amoeba();
  }

}
