import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class CurveFitII extends Applet implements MouseListener, MouseMotionListener {
	
	double	x[];
	double	y[];
	int selected;
	
	public void init() {
		selected = 0;
		x = new double[4];
		y = new double[4];
		x[1] = 40;
		y[1] = 150;
		x[2] = 75;
		y[2] = 75;
		x[3] = 150;
		y[3] = 40;
		
		setBackground( Color.black );
		addMouseListener(this);
		addMouseMotionListener(this);
	}
	
	public void mousePressed(MouseEvent e) {
		int	mx = e.getX();
		int	my = e.getY();
		Ellipse2D check;
		for(int i = 0; i < 4; i++)
		{
			check = new Ellipse2D.Double(x[i]-6,y[i]-6,11,11);
			if(check.contains(mx,my))
				selected = i;
		}
	}
	
	public void mouseReleased(MouseEvent e) {
		selected = 0;
	}
	
	public void mouseEntered(MouseEvent e) {
	}
	
	public void mouseExited(MouseEvent e) {
	}
	
	public void mouseClicked(MouseEvent e) {
	}
	
	public void mouseMoved(MouseEvent e) {
	}
	
	public void mouseDragged(MouseEvent e) {
		x[selected] = e.getX();
		y[selected] = e.getY();
		repaint();
	}
	
	public void fillOval(Graphics g, double xc, double yc, double size) {
		g.fillOval((int)(xc-(size/2.0)),(int)(yc-(size/2.0)), (int)(size), (int)(size));
	}

	public void paint( Graphics g )
	{
		Point2D	p1 = new Point2D.Double(x[1],y[1]);
		Point2D	p2 = new Point2D.Double(x[2],y[2]);
		Point2D	p3 = new Point2D.Double(x[3],y[3]);
		Ellipse2D c = makeCircle(p1,p2,p3);
		
		// Fill circles at the 3 points used
		g.setColor( Color.green );
		fillOval(g,x[1],y[1],7);
		fillOval(g,x[2],y[2],7);
		fillOval(g,x[3],y[3],7);
		
		// Draw lines between those three points
		g.drawLine((int)x[1],(int)y[1],(int)x[2],(int)y[2]);
		g.drawLine((int)x[2],(int)y[2],(int)x[3],(int)y[3]);
		
		if(c != null)
		{
			double	cx = c.getX() + c.getWidth()/2.0;
			double	cy = c.getY() + c.getHeight()/2.0;
			
			g.setColor( Color.blue );
			
			// Fill the center point
			fillOval(g,cx,cy,7);
			
			// Draw lines from p1, p2, p3 to the center
			g.drawLine((int)x[1],(int)y[1],(int)cx,(int)cy);
			g.drawLine((int)x[2],(int)y[2],(int)cx,(int)cy);
			g.drawLine((int)x[3],(int)y[3],(int)cx,(int)cy);
			
			// Draw the circle
			g.drawOval((int)c.getX(),(int)c.getY(),(int)c.getWidth(),(int)c.getHeight());
		}
	}
	
	// Get distance between two points
	public static double getDistanceOfPoints(double p1x, double p1y, double p2x, double p2y)
	{
		double	offsetX = p2x-p1x;
		double	offsetY = p2y-p1y;
		return Math.sqrt(Math.pow(offsetX,2)+Math.pow(offsetY,2));
	}

	// Get the cross product of two vectors
	public static double getCrossProduct(Point2D p1, Point2D p2)
	{
		return (p1.getX() * p2.getY() - p1.getY() * p2.getX());
	}
	
	// Make a circle from three points (Will return null if points are coplanar)
	public Ellipse2D makeCircle(Point2D p1, Point2D p2, Point2D p3)
	{
		Ellipse2D circle = null;
		// vector of edge opposite of p2
		Point2D	  v2 = new Point2D.Double(p2.getX()-p1.getX(),p2.getY()-p1.getY());
		// vector of edge opposite of p3
		Point2D	  v3 = new Point2D.Double(p3.getX()-p1.getX(),p3.getY()-p1.getY());
		
		// Check the cross product to ensure the points are not coplanar
		if (getCrossProduct(v2,v3) != 0.0)
		{
			double	a = p1.getX();
			double	b = p1.getY();
			double	c = p2.getX();
			double	d = p2.getY();
			double	e = p3.getX();
			double	f = p3.getY();
			double	absq = (a*a+b*b);
			double	cdsq = (c*c+d*d);
			double	efsq = (e*e+f*f);
			double	cx = 0;
			double	cy = 0;
			double	cr = 0;
			
			// Calculate the X coordinate of the center point
			cx = (absq*(f-d)+cdsq*(b-f)+efsq*(d-b))/(2.0*(a*(f-d)+c*(b-f)+e*(d-b)));
			
			// Calculate the Y coordinate of the center point
			cy = (absq*(e-c)+cdsq*(a-e)+efsq*(c-a))/(2.0*(b*(e-c)+d*(a-e)+f*(c-a)));
			
			// Calculate the radius of the circle
			cr = getDistanceOfPoints(cx,cy,a,b);
			
			// Initialize the circle
			circle = new Ellipse2D.Double(cx-cr,cy-cr,2*cr,2*cr);
		}
		
		return circle;
	}
}

