
import java.applet.Applet;
import java.awt.Button;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

/*
 * The methods getX(), getY() and so on in the Point, Rectangle and Dimension classes
 * are not implemented in browser versions of Java (to hell with them :(
 * therefor the public variables must be used.
 *	
 * @version 1.01, 15/02/2001
 * @author 	Håvard Rast Blok
 */

public class Casteljau extends Applet implements ActionListener
{
	private Button draw;
	private MyCanvas canvas;
	private MouseHandler mHandler;
	
	private boolean mouseMoving;
	private int movingPoint;
	private boolean animate;
	
	private Point ctrlPoints[];
	private Rectangle ctrlRect[];
	private Dimension pointSize;
	private int pointSizeX;
	private int pointSizeY;

	private double t;
	private double step = 0.02;
	
	public void init()
	{
		resize(300,300);
		
		//make init control points
		ctrlPoints = new Point[4];
		ctrlPoints[0] = new Point( 50, 250 );
		ctrlPoints[1] = new Point( 75, 50 );
		ctrlPoints[2] = new Point( 200, 50 );
		ctrlPoints[3] = new Point( 250, 250 );
		
		//set size of the control points on screen
		pointSize = new Dimension(10, 10);
		pointSizeX = 10;
		pointSizeY = 10;
		
		//make the init rectangles sorrounding the control points
		//these are used to ease the checking of mouse selection
		ctrlRect = new Rectangle[4];
		makeRectangles();
		
		//add components to screen
		setLayout( new BorderLayout() );
		
		draw = new Button( "Animate!" );
		canvas = new MyCanvas();
		canvas.setBackground( new Color( 255, 255, 255 ) );
		
		add( draw, BorderLayout.SOUTH );
		add( canvas, BorderLayout.CENTER );

		draw.addActionListener( this );

		//add mouse listener
		mHandler = new MouseHandler();
		canvas.addMouseListener( mHandler );
		canvas.addMouseMotionListener( mHandler );
		
		//showStatus( "Casteljau.init - end");
		//draw.setLabel( "Casteljau.init - end");
	}
	
	private void makeRectangles()
	{
		Point tmpPoint;

		for(int i = 0; i < 4; i++)
		{
			tmpPoint = new Point( ctrlPoints[i] );
			//tmpPoint.translate( -(int)pointSize.getWidth()/2, -(int)pointSize.getHeight()/2 );
			tmpPoint.translate( -pointSizeX/2, -pointSizeY/2 );
			ctrlRect[i] = new Rectangle( tmpPoint, pointSize );
		}
	}
	
	public void actionPerformed( ActionEvent e )
	{
		//start the animation
		animate = true;
		canvas.repaint();
		t = 0.0;
	}

	//internal class for canvas
	class MyCanvas extends Canvas
	{
		private final int pointRadius = 5;
		
		//start/end points of guide lines
		private Point a, b, c, d, e;
		
		public void paint(Graphics g)
		{
			Point p;
			
			//draw.setLabel( "MyCanvas.paint - start");
			
			//draw control points
			g.setColor( new Color( 255, 0, 0 ) );
			for(int i = 0; i < ctrlPoints.length; i++)
			{
				//g.fillOval( (int)ctrlRect[i].getX(), (int)ctrlRect[i].getY(),
				//				(int)ctrlRect[i].getWidth(), (int)ctrlRect[i].getHeight() );
				g.fillOval( ctrlRect[i].x, ctrlRect[i].y,
								ctrlRect[i].width, ctrlRect[i].height );
				
			}
			
			//draw lines between
			for(int i = 0; i < 3; i++)
			{
				//g.drawLine( (int)ctrlPoints[i].getX(), (int)ctrlPoints[i].getY(),
				//				(int)ctrlPoints[i+1].getX(), (int)ctrlPoints[i+1].getY() );
				g.drawLine( ctrlPoints[i].x, ctrlPoints[i].y,
								ctrlPoints[i+1].x, ctrlPoints[i+1].y );
				
			}

			if( animate )
			{
				//draw bezier curve up to this point
				//returns the last dran point on the curve
				p = drawCurve(g);
				
				//draw guide lines
				g.setColor( new Color( 0, 0, 0 ) );
				drawLine( g, a, b );
				drawLine( g, b, c );
				drawLine( g, d, e );
			
				//draw point p
				g.setColor( new Color( 0, 0, 255 ) );
				//g.fillOval( (int)p.getX()-3, (int)p.getY()-3, 6, 6 );
				g.fillOval( p.x-3, p.y-3, 6, 6 );
				
				t += step;
				
				//stop animation?
				if( t>1.0+step )
				{
					animate = false;
				}
				else
				{
					//wait a while
					try
					{
						Thread.sleep( 200 );
					}
					catch(Exception ex) { } //do nothing
				
					repaint();
				}
			}

			//draw.setLabel( "MyCanvas.paint - end");
		}
		
		/**
		 * Draw the Bezier curve up to this point (t)
		 * @return last drawn point on curve
		 */
		private Point drawCurve( Graphics g )
		{
			Point ans = null;
			
			for(double i = 0; i <= t; i += step)
			{
				//compute guide lines
				a = getPoint( ctrlPoints[0], ctrlPoints[1], i );
				b = getPoint( ctrlPoints[1], ctrlPoints[2], i );
				c = getPoint( ctrlPoints[2], ctrlPoints[3], i );
				d = getPoint( a, b, i );
				e = getPoint( b, c, i );
			
				ans = getPoint( d, e, i );
				
				//draw point ans
				g.setColor( new Color( 0, 0, 0 ) );
				//g.fillOval( (int)ans.getX()-2, (int)ans.getY()-2, 4, 4 );
				g.fillOval( ans.x-2, ans.y-2, 4, 4 );
				
			}
			
			return ans;
		}
		
		/**
		 * Computes a point on a line between start and end
		 * @param start Start point of line.
		 * @param end End point of line.
		 * @param t Functio parameter between 0.0 and 1.0
		 * @return point on line
		 */
		
		private Point getPoint( Point start, Point end, double t )
		{
			int x;
			int y;
			
			//x = (int)getPointComponent( start.getX(), end.getX(), t );
			//y = (int)getPointComponent( start.getY(), end.getY(), t );
			x = (int)getPointComponent( start.x, end.x, t );
			y = (int)getPointComponent( start.y, end.y, t );
			
			
			return new Point(x, y);
		}
		
		/**
		 * Computes a component of a point on a line
		 * @param start Starting point of line
		 * @param end End point of line
		 * @param t Function parameter between 0.0 and 1.0
		 * @return one compoent of a point on the line between start and ned
		 */
		
		private double getPointComponent( double start, double end, double t)
		{
			return (1-t)*start + t*end;
		}
		
		/**
		 * Draws a line between start and end using the Graphics object g
		 * @param g Graphics object of component to draw upon
		 * @param start start point of line
		 * @param end end point of line
		 */
		
		private void drawLine( Graphics g, Point start, Point end )
		{
			//g.drawLine( (int)start.getX(), (int)start.getY(), 
			//				(int)end.getX(), (int)end.getY() );
			g.drawLine( start.x, start.y, 
							end.x, end.y );
		}
	}
	
	//internal class to handle mouse moves
	class MouseHandler implements MouseListener, MouseMotionListener
	{
		/** methods required for MouseListener and MouseMotionListener interfaces */
		public void mouseReleased(MouseEvent e)
		{
			/** object is not moving anymore */
			mouseMoving=false;
		}
		
		public void mouseEntered(MouseEvent e) {}
		
		public void mouseExited(MouseEvent e)
		{
			//disable moving when outside window
			mouseMoving=false;
		}
		
		public void mouseMoved(MouseEvent e) {}
		public void mouseClicked(MouseEvent e) {}
		
		public void mousePressed(MouseEvent e)
		{
			Point pressedPoint;
			
			//System.out.println("MouseHandler.mousePressed: "+e);
			
			//disable moving the points while animating
			if( !animate )
			{
				
				pressedPoint = new Point( e.getX(), e.getY() );
				
				//check if point was inside a control rectangle
				for(int i = 0; i < 4; i++)
				{
					if( ctrlRect[i].contains( pressedPoint ) )
					{
						mouseMoving=true;
						movingPoint=i;
						break;
					}
				}
				
				//System.out.println("MouseHandler.mousePressed - dragging point "+movingPoint);
			}
		}
	
		public void mouseDragged(MouseEvent e)
		{
			Point draggedPoint;

			//if a control point was selected...
			if( mouseMoving )
			{
				//move the point, make new rectangels and repaint canvas
				draggedPoint = new Point( e.getX(), e.getY() );
				ctrlPoints[ movingPoint ].setLocation( draggedPoint );
				makeRectangles();
				canvas.repaint();
			}
		}
	}
}


