import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class CurveFit 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 ) {
		// Midpoint between p1 and p2
		double pDX = 0.0;
		double pDY = 0.0;
		// Midpoint between p0 and p1
		double pEX = 0.0;
		double pEY = 0.0;
		double mAB = 0.0; // Slope of p1 to p2
		double mBC = 0.0; // Slope of p2 to p3
		double mDF = 0.0; // Slope from mid of p1-p2 to center of circle (center line 2)
		double mEF = 0.0; // Slope from mid of p2-p3 to center of circle (center line 1)
		boolean	iDF = false; // Slope is vertical/infinity
		boolean	iEF = false; // Slope is vertical/infinity
		double bDF = 0.0; // X-intercept of center line 2
		double bEF = 0.0; // X-intercept of center line 1
		// Center point of the curve
		double pFX = 0.0;
		double pFY = 0.0;
		
		g.setColor( Color.green );
		
		// Fill circles at the 3 points used
		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]);
		
		g.setColor( Color.red );
		
		// Calculate the midpoints between the main points
		pDX = x[1] + ((x[2]-x[1])/2.0);
		pDY = y[1] + ((y[2]-y[1])/2.0);
		pEX = x[2] + ((x[3]-x[2])/2.0);
		pEY = y[2] + ((y[3]-y[2])/2.0);

		// Fill circles at the midpoints of those two lines
		fillOval(g,pDX,pDY,7);
		fillOval(g,pEX,pEY,7);
		
		// Get the slope perpendicular to the line between p1 and p2
		if( (x[2]-x[1]) == 0 )
		{
			mDF = 0;
		}
		else if( (y[2]-y[1]) != 0 )
		{
			mAB = ((y[2]-y[1])/(x[2]-x[1]));
			mDF = -1/mAB;
		}
		else
		{
			iDF = true;
		}
		
		// Get the slope perpendicular to the line between p2 and p3
		if( (x[3]-x[2]) == 0 )
		{
			mEF = 0;
		}
		else if( (y[3]-y[2]) != 0 )
		{
			mBC = ((y[3]-y[2])/(x[3]-x[2]));
			mEF = -1/mBC;
		}
		else 
		{
			iEF = true;
		}
		
		// Calculate the x-intercepts of the lines to the center
		bDF = -1 * (mDF*pDX - pDY);
		bEF = -1 * (mEF*pEX - pEY);
		
		// If the lines are perpendicular, there will be no center point
		if( (mDF == mEF) && (iDF == iEF))
			return;
		
		// Calculate the center point of the circle
		if(iDF == true)
		{
			pFX = pDX;
			pFY = mEF*pFX + bEF;
		}
		else if(iEF == true)
		{
			pFX = pEX;
			pFY = mDF*pFX + bDF;
		}
		else
		{
			pFX = (bEF - bDF)/(mDF - mEF);
			pFY = mDF*pFX + bDF;
		}
		
		// Draw lines to the center
		g.setColor( Color.red );
		g.drawLine((int)pDX,(int)pDY,(int)pFX,(int)pFY);
		g.drawLine((int)pEX,(int)pEY,(int)pFX,(int)pFY);
		
		// Fill the center point
		g.setColor( Color.blue );
		fillOval(g,pFX,pFY,7);
		
		// Draw lines from p1, p2, p3 to the center
		g.drawLine((int)x[1],(int)y[1],(int)pFX,(int)pFY);
		g.drawLine((int)x[2],(int)y[2],(int)pFX,(int)pFY);
		g.drawLine((int)x[3],(int)y[3],(int)pFX,(int)pFY);
		
		// Get the radius of the circle
		double	R = Math.sqrt(Math.pow(x[1]-pFX,2)+Math.pow(y[1]-pFY,2));
		
		// Draw the circle itself
		g.drawOval((int)(pFX-R),(int)(pFY-R),(int)R*2,(int)R*2);
	}
}
