import ij.*;
import ij.gui.*;
import ij.measure.Calibration;
import ij.plugin.filter.PlugInFilter;
import ij.process.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

/** Bob Dougherty.
	This plugin makes a elliptical ROI by dragging the cursor across one axis, and then editing.
	This capability was requested by Mark Hayworth.
	The plugin uses the blank spot in the toolbar between the text and zoom tools.
	This beta release has some known limitations:
	1. This is a PluginFilter.  It only works on the image that was in front when the plugin was run.
	2. Threads are used to orchestrate the work.  This is effective, but it would be more
	   efficient to use events.  A future version may use events.
	3. The tool in the toolbar (between the text and zoom tools, as noted) looks blank.
	Version 0 7/14/2002.
	Version 1 7/14/2002.  Improved shutDown.
	Version 2 2/28/2004.  Changed Roi type to Roi.TRACED_ROI to remove little squares.
	Version 3 2/28/2004.  Added status bar output.
	Version 4 2/29/2004.  Shut down if another selection tool selected (Gabriel Landini suggestion),
						  Improved status bar formatting (Gabriel Landini suggestions.
	Version 5 3/1/2004    Revised calibration scaling for the anistropic case.
	Version 6 3/1/2004    Similar to version 5, but correct this time.*/
/*	License:
	Copyright (c) 2004, 2005, OptiNav, Inc.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions
	are met:

		Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.
		Redistributions in binary form must reproduce the above copyright
	notice, this list of conditions and the following disclaimer in the
	documentation and/or other materials provided with the distribution.
		Neither the name of OptiNav, Inc. nor the names of its contributors
	may be used to endorse or promote products derived from this software
	without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public class Ellipse_Roi implements PlugInFilter {
	ImagePlus imp;

	public int setup(String arg, ImagePlus imp) {
		this.imp = imp;
		return DOES_ALL;
	}

	public void run(ImageProcessor ip) {
		final ImageCanvas ic = imp.getWindow().getCanvas();
		final EllipseRoi cr = new EllipseRoi(imp,ic);
		imp.updateAndDraw();
	}
}

class EllipseRoi extends ImageCanvas implements MouseListener, MouseMotionListener, Runnable {
	ImageCanvas ic;
	Graphics g;
	Toolbar tb;


	//Parameters describing ellipse.
	//Note: the axes are constrained to be orthogonal.  In vector notation, this means
	// (D - C) dot (B - A) = 0.
	//They are also constrained to be symmetric:
	// C + D = A + B.
	double xA, yA; //End point of first axis, postive side
	double xB, yB; //End point of first axis, negative side
	double xC, yC; //End point of axis A, negative side
	double xD, yD; //End point of axis B, negative side
	double xCent, yCent; //Center point.  Average of A and B or C and D.
	double xStartMoveCent,yStartMoveCent;


	static int NUMPOLY = 1000;
	int[] xPoints, yPoints;
	boolean done = false;

	double xAold, yAold;
	double xBold, yBold;
	double xCold, yCold;
	double xDold, yDold;
	double xCentOld, yCentOld;

	double xCInput,yCInput;
	double xDInput,yDInput;

	double Acal,Ccal,thetaCal;

	int napTime = 100;
	boolean ellipseOn = false;
	boolean started = false;
	boolean movingA = false;
	boolean movingB = false;
	boolean movingC = false;
	boolean movingD = false;
	boolean movingCent = false;
	boolean circular = true;
	boolean changed = false;

	protected Thread thread;
	protected long lastUseTime;

	public EllipseRoi(ImagePlus imp, ImageCanvas ic) {
		super(imp);
		this.imp = imp;
		this.ic = ic;
		xPoints = new int[NUMPOLY];
		yPoints = new int[NUMPOLY];
		g = ic.getGraphics();
		g.setColor(Color.yellow);
		tb = Toolbar.getInstance();
		tb.addTool("Ellipse (limited to "+imp.getShortTitle()+")");
		tb.setTool(Toolbar.SPARE1);
		thread = new Thread(this, "EllipseRoi");

		//Set the ellipse off the image, so the first click will start a new one.
		xA = -1000;
		xAold = xA;
		yA = -1000;
		yAold = yA;
		xB = -1000;
		xBold = xB;
		yB = -1000;
		yBold = yB;
		xC = -1000;
		xCold = xC;
		yC = -1000;
		yCold = yC;
		xD = -1000;
		xDold = xD;
		yD = -1000;
		yDold = yD;
		xCent = -1000;
		xCentOld = xCent;
		yCent = -1000;
		yCentOld = yCent;

		thread.start();
		turnEllipseOn();
	}

	public void run() {
		while (!done) {
			try {Thread.sleep(napTime);}
			catch(InterruptedException e) {
				shutDown();
				break;
			}
			int toolID = tb.getToolId();
			if((toolID == Toolbar.FREELINE)||
				(toolID == Toolbar.FREEROI)||
				(toolID == Toolbar.LINE)||
				(toolID == Toolbar.OVAL)||
				(toolID == Toolbar.POLYGON)||
				(toolID == Toolbar.POLYLINE)||
				(toolID == Toolbar.RECTANGLE)){
				shutDown();
				break;
			}
			if (ic == null){
				shutDown();
			} else if (!ic.isValid()){
				shutDown();
				break;
			}
			if (g == null){
				shutDown();
				break;
			}
			if (started&&ellipseOn){
				updateRoi();
				updateStatus();
			} else {
				if((toolID == Toolbar.SPARE1)&&(!ellipseOn)) turnEllipseOn();
			}
			if((System.currentTimeMillis()-lastUseTime) < 200){
				napTime = 10;
			} else if (ellipseOn){
				napTime = 100;
			} else {
				napTime = 200;
			}
		}
	}

	private void updateStatus(){
		double A = Math.sqrt((xA - xB)*(xA - xB) + (yA - yB)*(yA - yB));
		double C = Math.sqrt((xC - xD)*(xC - xD) + (yC - yD)*(yC - yD));
		double theta = -180*Math.atan2(yA - yB, xA - xB)/Math.PI;


		Calibration cal = imp.getCalibration();
		 String size;
		if (cal.scaled() && !IJ.altKeyDown()){
			//double Acal = Math.sqrt((xA - xB)*(xA - xB)*cal.pixelWidth*cal.pixelWidth
			//	+ (yA - yB)*cal.pixelHeight*(yA - yB)*cal.pixelHeight);
			//double Ccal = Math.sqrt((xC - xD)*(xC - xD)*cal.pixelWidth*cal.pixelWidth
			//	+ (yC - yD)*(yC - yD)*cal.pixelHeight*cal.pixelHeight);
			double thetaCalEst = -180*Math.atan2((yA - yB)*cal.pixelHeight,
				(xA - xB)*cal.pixelWidth)/Math.PI;
			computeScaledParameters(cal,thetaCalEst);
			size = "A="+IJ.d2s(Acal)+" ("+IJ.d2s(A)+")"+
					", B="+IJ.d2s(Ccal)+" ("+IJ.d2s(C)+")"+
 					", theta="+IJ.d2s(thetaCal)+" ("+IJ.d2s(theta)+")";
       }else{
			size = "x="+IJ.d2s(xCent,0)+", y="+IJ.d2s(yCent,0)+
				", A="+IJ.d2s(A)+", B="+IJ.d2s(C,1)+", theta="+IJ.d2s(theta);
		}
		IJ.showStatus(size);
	}
	//Transform an ellipse from pixel coordinates to scaled coordinates
	private void computeScaledParameters(Calibration cal,double thetaCalEst){
		//Semi-axes in pixel coordinates
		double asq = ((xA - xB)*(xA - xB) + (yA - yB)*(yA - yB))/4;
		double bsq = ((xC - xD)*(xC - xD) + (yC - yD)*(yC - yD))/4;
		if ((asq==0)||(bsq==0)){
			Acal = 0;
			Ccal = 0;
			thetaCal = 0;
			return;
		}
		//Compute matrix elements in rotated coordinates
		double theta = -Math.atan2(yA - yB, xA - xB);
		double c = Math.cos(theta);
		double s = Math.sin(theta);
		double a11 = c*c/asq + s*s/bsq;
		double a12 = s*c*(1/bsq - 1/asq);
		double a22 = c*c/bsq + s*s/asq;
		//Scale
		a11 /= cal.pixelWidth*cal.pixelWidth;
		a12 /= cal.pixelWidth*cal.pixelHeight;
		a22 /= cal.pixelHeight*cal.pixelHeight;
		//Find eigenvalues
		double root = (a11 + a22)*(a11 + a22) + 4*(a12*a12 - a11*a22);
		if (root < 0){
			Acal = 0;
			Ccal = 0;
			thetaCal = 0;
			return;
		}
		root = Math.sqrt(root);
		double lambda1 = (a11 + a22 + root)/2;
		double lambda2 = (a11 + a22 - root)/2;
		if (lambda2 <= 0){
			Acal = 0;
			Ccal = 0;
			thetaCal = 0;
			return;
		}
		double theta1 = -Math.atan2(lambda1 - a11 ,a12); //Angle of major scaled axis
		double Acal1 = 2/Math.sqrt(lambda1);
		double Ccal1 = 2/Math.sqrt(lambda2);
		//Check for assignment problems.  Theta goes from -180 to 180, and should be
		//close to thetaCalEst.
		double cE = Math.cos(Math.PI*thetaCalEst/180);
		double sE = Math.sin(Math.PI*thetaCalEst/180);
		double dot1 = Math.cos(theta1)*cE + Math.sin(theta1)*sE;
		if (dot1 > 0.707106){
			thetaCal = 180*theta1/Math.PI;
			Acal = Acal1;
			Ccal = Ccal1;
		}else if(dot1 < -0.707106){
			thetaCal = 180*theta1/Math.PI;
			thetaCal += 180;
			if(thetaCal > 180) thetaCal -= 360;
			Acal = Acal1;
			Ccal = Ccal1;
		}else{
			theta1 += Math.PI/2;
			dot1 = Math.cos(theta1)*cE + Math.sin(theta1)*sE;
			if (dot1 > 0.707106){
				thetaCal = 180*theta1/Math.PI;
				Acal = Ccal1;
				Ccal = Acal1;
			}else{
				thetaCal = 180*theta1/Math.PI;
				thetaCal += 180;
				Acal = Ccal1;
				Ccal = Acal1;
			}
			if(thetaCal > 180) thetaCal -= 360;
		}
		return;
	}

	private void updateRoi(){
		double distAB,distCD;
		changed = false;
		if((xA != xAold)||(yA != yAold)){
			if(circular){
				distCD = Math.sqrt((xA - xB)*(xA - xB) + (yA - yB)*(yA - yB));
			} else {
				distCD = Math.sqrt((xC - xD)*(xC - xD) + (yC - yD)*(yC - yD));
			}
			updateCD(distCD);
			changed = true;
		} else if ((xB != xBold)||(yB != yBold)){
			if(circular){
				distCD = Math.sqrt((xA - xB)*(xA - xB) + (yA - yB)*(yA - yB));
			} else {
				distCD = Math.sqrt((xC - xD)*(xC - xD) + (yC - yD)*(yC - yD));
			}
			updateCD(distCD);
			changed = true;
		} else if (movingC&&((xC != xCInput)||(yC != yCInput))){
			updateC();
			xCInput = xC;
			yCInput = yC;
			changed = true;
		} else if (movingD&&((xD != xDInput)||(yD != yDInput))){
			updateD();
			xDInput = xD;
			yDInput = yD;
			changed = true;
		} else if ((xCent != xCentOld)||(yCent != yCentOld)){
			updateCenter();
			changed = true;
		}
		if (changed){
			drawEllipse();
			lastUseTime = System.currentTimeMillis();
			xAold = xA;
			yAold = yA;
			xBold = xB;
			yBold = yB;
			xCold = xC;
			yCold = yC;
			xDold = xD;
			yDold = yD;
			xCentOld = xCent;
			yCentOld = yCent;
		}
		drawHandles();
	}

	public void drawEllipse(){
		//imp.killRoi();
		double hTheta = 2*Math.PI/NUMPOLY;
		double ax = xA - xCent;
		double ay = yA - yCent;
		double cx = xC - xCent;
		double cy = yC - yCent;
		double a4 = (ax*ax + ay*ay)*(ax*ax + ay*ay);
		double c4 = (cx*cx + cy*cy)*(cx*cx + cy*cy);
		if ((a4 == 0) && (c4 == 0))return;
		if (a4 == 0)a4 = 1;
		if (c4 == 0)c4 = 1;
		for (int i = 0; i < NUMPOLY; i++){
			double theta = i*hTheta;
			double cos = Math.cos(theta);
			double sin = Math.sin(theta);
			double r = Math.sqrt(1/((ax*cos + ay*sin)*(ax*cos + ay*sin)/a4
						 + (cx*cos + cy*sin)*(cx*cos + cy*sin)/c4));
			xPoints[i] = (int)Math.round(xCent + r*cos);
			yPoints[i] = (int)Math.round(yCent + r*sin);
		}
		//PolygonRoi proi = new PolygonRoi(xPoints, yPoints,NUMPOLY,Roi.POLYGON);
		PolygonRoi proi = new PolygonRoi(xPoints, yPoints,NUMPOLY,Roi.TRACED_ROI);
		//proi.fitSpline(NUMSPLINE);
		imp.setRoi(proi);

	}

	public void drawHandles(){
		//ic.paint(g);
		g.drawLine(ic.screenX((int)xA), ic.screenY((int)yA), ic.screenX((int)xB), ic.screenY((int)yB));
		drawCircle5(g,(int)xA,(int)yA);
		drawCircle5(g,(int)xB,(int)yB);
		drawCircle5(g,(int)xC,(int)yC);
		drawCircle5(g,(int)xD,(int)yD);

		//Draw the tic mark in the center.
		double xDhat = xD - xCent;
		double yDhat = yD - yCent;
		double axis = Math.sqrt(xDhat*xDhat + yDhat*yDhat);
		if (axis == 0) axis = 1;
		int TIC = 8;
		int dx = (int)(TIC*xDhat/axis);
		int dy = (int)(TIC*yDhat/axis);
		g.drawLine(ic.screenX((int)xCent) - dx, ic.screenY((int)yCent) - dy,
				 ic.screenX((int)xCent) + dx, ic.screenY((int)yCent) + dy);
	}

	public void drawCircle5(Graphics g, int x,  int y){
		g.drawLine(ic.screenX(x) - 2, ic.screenY(y) - 7, ic.screenX(x) + 2, ic.screenY(y) - 7);//1
		g.drawLine(ic.screenX(x) + 7, ic.screenY(y) - 2, ic.screenX(x) + 7, ic.screenY(y) + 2);//2
		g.drawLine(ic.screenX(x) - 2, ic.screenY(y) + 7, ic.screenX(x) + 2, ic.screenY(y) + 7);//3
		g.drawLine(ic.screenX(x) - 7, ic.screenY(y) + 2, ic.screenX(x) - 7, ic.screenY(y) - 2);//4
		g.drawLine(ic.screenX(x) - 4, ic.screenY(y) - 6, ic.screenX(x) - 3, ic.screenY(y) - 6);//5
		g.drawLine(ic.screenX(x) + 4, ic.screenY(y) - 6, ic.screenX(x) + 3, ic.screenY(y) - 6);//6
		g.drawLine(ic.screenX(x) + 6, ic.screenY(y) - 3, ic.screenX(x) + 6, ic.screenY(y) - 4);//7
		g.drawLine(ic.screenX(x) + 6, ic.screenY(y) + 3, ic.screenX(x) + 6, ic.screenY(y) + 4);//8
		g.drawLine(ic.screenX(x) - 4, ic.screenY(y) + 6, ic.screenX(x) - 3, ic.screenY(y) + 6);//9
		g.drawLine(ic.screenX(x) + 4, ic.screenY(y) + 6, ic.screenX(x) + 3, ic.screenY(y) + 6);//10
		g.drawLine(ic.screenX(x) - 6, ic.screenY(y) - 3, ic.screenX(x) - 6, ic.screenY(y) - 4);//11
		g.drawLine(ic.screenX(x) - 6, ic.screenY(y) + 3, ic.screenX(x) - 6, ic.screenY(y) + 4);//12
		g.drawLine(ic.screenX(x) - 5, ic.screenY(y) - 5, ic.screenX(x) - 5, ic.screenY(y) - 5);//13
		g.drawLine(ic.screenX(x) + 5, ic.screenY(y) - 5, ic.screenX(x) + 5, ic.screenY(y) - 5);//14
		g.drawLine(ic.screenX(x) + 5, ic.screenY(y) + 5, ic.screenX(x) + 5, ic.screenY(y) + 5);//15
		g.drawLine(ic.screenX(x) - 5, ic.screenY(y) + 5, ic.screenX(x) - 5, ic.screenY(y) + 5);//16
	}

	//Establish C and D to be the given distance apart, with A and B given.
	public void updateCD(double distCD){
		xCent = (xA + xB)/2;
		yCent = (yA + yB)/2;
		double xAhat = xA - xCent;
		double yAhat = yA - yCent;
		double axis = Math.sqrt(xAhat*xAhat + yAhat*yAhat);
		if (axis == 0) axis = 1;
		xAhat /= axis;
		yAhat /= axis;
		axis = distCD/2;
		xC = xCent + yAhat*axis;
		yC = yCent - xAhat*axis;
		xD = xCent - yAhat*axis;
		yD = yCent + xAhat*axis;
	}

	//Determine C and D from A, B, and an effort at D.
	public void updateD(){
		xCent = (xA + xB)/2;
		yCent = (yA + yB)/2;
		double xAhat = xA - xCent;
		double yAhat = yA - yCent;
		double axis = Math.sqrt(xAhat*xAhat + yAhat*yAhat);
		if (axis == 0) axis = 1;
		xAhat /= axis;
		yAhat /= axis;
		double xDhat = yAhat;
		double yDhat = -xAhat;
		double xOffset = xDInput - xCent;
		double yOffset = yDInput - yCent;
		double halfWidth = Math.sqrt(xOffset*xOffset + yOffset*yOffset);  //Could do projection.
		xC = xCent + halfWidth*xDhat;
		yC = yCent + halfWidth*yDhat;
		xD = xCent - halfWidth*xDhat;
		yD = yCent - halfWidth*yDhat;
	}

	//Determine C and D from A, B, and an effort at D.
	public void updateC(){
		xCent = (xA + xB)/2;
		yCent = (yA + yB)/2;
		double xAhat = xA - xCent;
		double yAhat = yA - yCent;
		double axis = Math.sqrt(xAhat*xAhat + yAhat*yAhat);
		if (axis == 0) axis = 1;
		xAhat /= axis;
		yAhat /= axis;
		double xDhat = yAhat;
		double yDhat = -xAhat;
		double xOffset = xCent - xCInput;
		double yOffset = yCent - yCInput;
		double halfWidth = Math.sqrt(xOffset*xOffset + yOffset*yOffset);  //Could do projection.
		xC = xCent + halfWidth*xDhat;
		yC = yCent + halfWidth*yDhat;
		xD = xCent - halfWidth*xDhat;
		yD = yCent - halfWidth*yDhat;
	}


	//Establish A and B to be the given distance apart, with C and D given.
	public void updateCenter(){
		double dx = xCent - xStartMoveCent;
		double dy = yCent - yStartMoveCent;
		xStartMoveCent = xCent;
		yStartMoveCent = yCent;
		xA += dx;
		yA += dy;
		xB += dx;
		yB += dy;
		if(circular){
			double distCD = Math.sqrt((xA - xB)*(xA - xB) + (yA - yB)*(yA - yB));
			updateCD(distCD);
		} else {
			double distCD = Math.sqrt((xC - xD)*(xC - xD) + (yC - yD)*(yC - yD));
			updateCD(distCD);
		}
	}

	private void shutDown(){
		if(ellipseOn){
			ic.removeMouseListener(this);
			ic.removeMouseMotionListener(this);
			ic.addMouseMotionListener(ic);
			ic.addMouseListener(ic);
			ellipseOn = false;
		}
		tb.addTool("Unassigned");
		this.setEnabled(false);
		ellipseOn = false;
		done = true;
		if(imp!=null)imp.updateAndDraw();
		this.setVisible(false);
		this.invalidate();
	}

	public void turnEllipseOff (){
		if(ellipseOn){
			if(ic != null){
				ic.removeMouseListener(this);
				ic.removeMouseMotionListener(this);
				ic.addMouseMotionListener(ic);
				ic.addMouseListener(ic);
			}
			ellipseOn = false;
			this.setEnabled(false);
			napTime = 100;
			imp.updateAndDraw();
		}
	}

	public void turnEllipseOn(){
		if(!ellipseOn){

			ic.removeMouseListener(ic);
			ic.removeMouseMotionListener(ic);
			ic.addMouseListener(this);
			ic.addMouseMotionListener(this);

			ellipseOn = true;
			this.setEnabled(true);
			napTime = 10;
			imp.updateAndDraw();
			lastUseTime = System.currentTimeMillis();
		}
	}

	public void mousePressed(MouseEvent e) {
		int xP = ic.offScreenX(e.getX());
		int yP = ic.offScreenY(e.getY());
		if(tb.getToolId() != Toolbar.SPARE1){
			turnEllipseOff();
			return;
		}
		final int A = 0;
		final int B = 1;
		final int C = 2;
		final int D = 3;

		//If the point is near one of the handles, start a move.  Otherwise start a new ellipse.

		double dA = (xP - xA)*(xP - xA) + (yP - yA)*(yP - yA);
		double dB = (xP - xB)*(xP - xB) + (yP - yB)*(yP - yB);
		double dC = (xP - xC)*(xP - xC) + (yP - yC)*(yP - yC);
		double dD = (xP - xD)*(xP - xD) + (yP - yD)*(yP - yD);
		double dCent = (xP - xCent)*(xP - xCent) + (yP - yCent)*(yP - yCent);

		double dMin = dA;
		int id = A;
		if(dB < dMin){
			id = B;
			dMin = dB;
		}
		if(dC < dMin){
			id = C;
			dMin = dC;
		}
		if(dD < dMin){
			id = D;
			dMin = dD;
		}

		movingA = false;
		movingB = false;
		movingC = false;
		movingB = false;
		movingCent = false;

		double mag = ic.getMagnification();

		//The mouse press must be within 8 pixels (on screen) of a handle to start a move or resize.
		if (dMin < 64/(mag*mag)){
			circular = false;
			switch (id) {
				case A:
					movingA = true;
					break;
				case B:
					movingB = true;
					break;
				case C:
					movingC = true;
					xCInput = xC;
					yCInput = yC;
					break;
				case D:
					movingD = true;
					xDInput = xD;
					yDInput = yD;
					break;
			}
		} else {
			Roi roi = imp.getRoi();
			if ((roi!=null)&&roi.contains(xP,yP)){
				movingCent = true;
				xStartMoveCent = xP;
				yStartMoveCent = yP;
			} else {
				circular = true;
				xA = xP;
				yA = yP;
				xB = xP;
				yB = yP;
				xC = xP;
				yC = yP;
				xCent = xP;
				yCent = yP;
				movingA = true;
				imp.killRoi();
			}
		}
	}

	public void mouseDragged(MouseEvent e) {
		if(movingA){
			xA = ic.offScreenX(e.getX());
			yA = ic.offScreenY(e.getY());
			started = true;
		} else if (movingB){
			xB = ic.offScreenX(e.getX());
			yB = ic.offScreenY(e.getY());
			started = true;
		} else if (movingC){
			xCInput = ic.offScreenX(e.getX());
			yCInput = ic.offScreenY(e.getY());
			started = true;
		} else if (movingD){
			xDInput = ic.offScreenX(e.getX());
			yDInput = ic.offScreenY(e.getY());
			started = true;
		} else if (movingCent){
			xCent = ic.offScreenX(e.getX());
			yCent = ic.offScreenY(e.getY());
			started = true;
		}
	}

	public void mouseExited(MouseEvent e) {
		int toolID = tb.getToolId();
		if(toolID != Toolbar.SPARE1) turnEllipseOff();//Cannot happen?
		ic.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
	}

	public void mouseEntered(MouseEvent e) {
		int toolID = tb.getToolId();
		if(toolID != Toolbar.SPARE1){
			turnEllipseOff();
		} else {
			ic.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
		}
	}

	public void mouseReleased(MouseEvent e) {
		movingA = false;
		movingB = false;
		movingC = false;
		movingD = false;
		movingCent = false;
	}
	public void mouseMoved(MouseEvent e) {}
	public void mouseClicked(MouseEvent e) {}

}
