/******************************************************
ParticlePicker V1.0
9-2-2003
*******************************************************/

import ij.gui.GUI;
import ij.gui.ImageCanvas;
import ij.gui.Roi;
import ij.gui.Toolbar;
import ij.gui.GenericDialog;
import ij.IJ;
import ij.ImagePlus;
import ij.measure.Calibration;
import ij.plugin.PlugIn;
import ij.WindowManager;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.CheckboxGroup;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Event;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.InputEvent;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Vector;
import ij.plugin.filter.ParticleAnalyzer;
import ij.process.ImageProcessor;
import ij.process.ImageStatistics;
import java.awt.image.*;
import ij.LookUpTable;

/*====================================================================
|	ParticlePicker_
\===================================================================*/

/*********************************************************************
 * This class is the only one that is accessed directly by imageJ;
 * it attaches listeners and dies. Note that it implements
 * <code>PlugIn</code> rather than <code>PlugInFilter</code>.
 ********************************************************************/
public class ParticlePicker_
	implements
		PlugIn

{ /* begin class ParticlePicker_ */

/*..................................................................*/
/* Public methods													*/
/*..................................................................*/

/*------------------------------------------------------------------*/
public void run (
	final String arg
) {
	final ImagePlus imp = WindowManager.getCurrentImage();
	if (imp == null) {
		IJ.noImage();
		return;
	}
	final ImageCanvas ic = imp.getWindow().getCanvas();
	final ParticleToolbar tb = new ParticleToolbar(Toolbar.getInstance());
	final ParticleHandler ph = new ParticleHandler(imp, tb);
	tb.setWindow(ph, imp);
} /* end run */

} /* end class ParticlePicker_ */

/*====================================================================
|	ParticleAction
\===================================================================*/

/*********************************************************************
 * This class is responsible for dealing with the mouse events relative
 * to the image window.
 ********************************************************************/
class ParticleAction
	extends
		ImageCanvas
	implements
		KeyListener,
		MouseListener,
		MouseMotionListener

{ /* begin class ParticleAction */

/*....................................................................
	Public variables
....................................................................*/
public static final int ADD_CIRCLE = 0;
public static final int MOVE_CIRCLE = 1;
public static final int FILE = 2;
public static final int TERMINATE = 5;
public static final int MAGNIFIER = 11;
public static final int FINDER = 3;
public static final int CLUSTER = 4;
public static final int HAND = 12;

/*....................................................................
	Private variables
....................................................................*/
private ImagePlus imp;
private ParticleHandler ph;
private ParticleToolbar tb;
private boolean frontmost = false;

/*....................................................................
	Public methods
....................................................................*/

/*********************************************************************
 * Return true if the window is frontmost.
 ********************************************************************/
public boolean isFrontmost (
) {
	return(frontmost);
} /* end isFrontmost */

/*********************************************************************
 * Listen to <code>keyPressed</code> events.
 *
 * @param e The expected key codes are as follows:
 * <ul><li><code>KeyEvent.VK_DELETE</code>: remove the current landmark;</li>
 * <li><code>KeyEvent.VK_BACK_SPACE</code>: remove the current landmark;</li>
 * <li><code>KeyEvent.VK_DOWN</code>: move down the current landmark;</li>
 * <li><code>KeyEvent.VK_LEFT</code>: move the current landmark to the left;</li>
 * <li><code>KeyEvent.VK_RIGHT</code>: move the current landmark to the right;</li>
 * <li><code>KeyEvent.VK_TAB</code>: activate the next landmark;</li>
 * <li><code>KeyEvent.VK_UP</code>: move up the current landmark.</li></ul>
 ********************************************************************/
public void keyPressed (
	final KeyEvent e
) {
	frontmost = true;
	final Point p = ph.getPoint();
	if (p == null) {
		frontmost = false;
		return;
	}
	final int x = p.x;
	final int y = p.y;
	switch (e.getKeyCode()) {
		case KeyEvent.VK_DELETE:
		case KeyEvent.VK_BACK_SPACE:
			ph.removePoint();
			break;
		case KeyEvent.VK_DOWN:
			ph.movePoint(imp.getWindow().getCanvas().screenX(x),
				imp.getWindow().getCanvas().screenY(y
				+ (int)Math.ceil(1.0 / imp.getWindow().getCanvas().getMagnification())));
			break;
		case KeyEvent.VK_LEFT:
			ph.movePoint(imp.getWindow().getCanvas().screenX(x
				- (int)Math.ceil(1.0 / imp.getWindow().getCanvas().getMagnification())),
				imp.getWindow().getCanvas().screenY(y));
			break;
		case KeyEvent.VK_RIGHT:
			ph.movePoint(imp.getWindow().getCanvas().screenX(x
				+ (int)Math.ceil(1.0 / imp.getWindow().getCanvas().getMagnification())),
				imp.getWindow().getCanvas().screenY(y));
			break;
		case KeyEvent.VK_TAB:
			ph.nextPoint();
			break;
		case KeyEvent.VK_UP:
			ph.movePoint(imp.getWindow().getCanvas().screenX(x),
				imp.getWindow().getCanvas().screenY(y
				- (int)Math.ceil(1.0 / imp.getWindow().getCanvas().getMagnification())));
			break;
	}
	imp.setRoi(ph);
} /* end keyPressed */

/*********************************************************************
 * Listen to <code>keyReleased</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void keyReleased (
	final KeyEvent e
) {
	frontmost = true;
} /* end keyReleased */

/*********************************************************************
 * Listen to <code>keyTyped</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void keyTyped (
	final KeyEvent e
) {
	frontmost = true;
} /* end keyTyped */

/*********************************************************************
 * Listen to <code>mouseClicked</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void mouseClicked (
	final MouseEvent e
) {
	frontmost = true;
} /* end mouseClicked */

/*********************************************************************
 * Listen to <code>mouseDragged</code> events. Move the current point
 * and refresh the image window.
 *
 * @param e Event.
 ********************************************************************/
public void mouseDragged (
	final MouseEvent e
) {
	frontmost = true;
	final int x = e.getX();
	final int y = e.getY();
	if (tb.getCurrentTool() == MOVE_CIRCLE) {
		ph.movePoint(x, y);
		imp.setRoi(ph);
	}
	mouseMoved(e);
} /* end mouseDragged */

/*********************************************************************
 * Listen to <code>mouseEntered</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void mouseEntered (
	final MouseEvent e
) {
	frontmost = true;
	WindowManager.setCurrentWindow(imp.getWindow());
	imp.getWindow().toFront();
	imp.setRoi(ph);
} /* end mouseEntered */

/*********************************************************************
 * Listen to <code>mouseExited</code> events. Clear the ImageJ status
 * bar.
 *
 * @param e Event.
 ********************************************************************/
public void mouseExited (
	final MouseEvent e
) {
	frontmost = false;
	imp.getWindow().toBack();
	IJ.getInstance().toFront();
	imp.setRoi(ph);
	IJ.showStatus("");
} /* end mouseExited */

/*********************************************************************
 * Listen to <code>mouseMoved</code> events. Update the ImageJ status
 * bar.
 *
 * @param e Event.
 ********************************************************************/
public void mouseMoved (
	final MouseEvent e
) {
	frontmost = true;
	setControl();
	final int x = imp.getWindow().getCanvas().offScreenX(e.getX());
	final int y = imp.getWindow().getCanvas().offScreenY(e.getY());
	IJ.showStatus(imp.getLocationAsString(x, y) + getValueAsString(x, y));
} /* end mouseMoved */

/*********************************************************************
 * Listen to <code>mousePressed</code> events. Perform the relevant
 * action.
 *
 * @param e Event.
 ********************************************************************/
public void mousePressed (
	final MouseEvent e
) {
	frontmost = true;
	final int x = e.getX();
	final int y = e.getY();
	int currentPoint;
	switch (tb.getCurrentTool()) {
		case ADD_CIRCLE:
		    if(e.getModifiers()==InputEvent.BUTTON1_MASK) {
				ph.addPoint(imp.getWindow().getCanvas().offScreenX(x),
				imp.getWindow().getCanvas().offScreenY(y));
				ph.FNone();
			}
			if(e.getModifiers()==InputEvent.BUTTON3_MASK) {
				ph.findClosest(x, y);
				ph.removePoint();
				ph.FPone();
            }

			break;
		case MAGNIFIER:
			final int flags = e.getModifiers();
			if ((flags & (Event.ALT_MASK | Event.META_MASK | Event.CTRL_MASK)) != 0) {
				imp.getWindow().getCanvas().zoomOut(x, y);
			}
			else {
				imp.getWindow().getCanvas().zoomIn(x, y);
			}
			break;
		case MOVE_CIRCLE:
			ph.findClosest(x, y);
			break;
		case HAND:
			int ox = imp.getWindow().getCanvas().offScreenX(x);
			int oy = imp.getWindow().getCanvas().offScreenY(y);
		   	//scroll(ox,oy);
            break;

	}
	imp.setRoi(ph);
} /* end mousePressed */

/*********************************************************************
 * Listen to <code>mouseReleased</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void mouseReleased (
	final MouseEvent e
) {
	frontmost = true;
} /* end mouseReleased */

/*********************************************************************
 * This constructor stores a local copy of its parameters and initializes
 * the current control.
 *
 * @param imp <code>ImagePlus<code> object where points are being picked.
 * @param ph <code>pointHandler<code> object that handles operations.
 * @param tb <code>pointToolbar<code> object that handles the toolbar.
 ********************************************************************/
public ParticleAction (
	final ImagePlus imp,
	final ParticleHandler ph,
	final ParticleToolbar tb
) {
	super(imp);
	this.imp = imp;
	this.ph = ph;
	this.tb = tb;
} /* end ParticleAction */

/*....................................................................
	Private methods
....................................................................*/

/*------------------------------------------------------------------*/
private String getValueAsString (
	final int x,
	final int y
) {
	final Calibration cal = imp.getCalibration();
	final int[] v = imp.getPixel(x, y);
	switch (imp.getType()) {
		case ImagePlus.GRAY8:
		case ImagePlus.GRAY16:
			final double cValue = cal.getCValue(v[0]);
			if (cValue==v[0]) {
				return(", value=" + v[0]);
			}
			else {
				return(", value=" + IJ.d2s(cValue) + " (" + v[0] + ")");
			}
		case ImagePlus.GRAY32:
			return(", value=" + Float.intBitsToFloat(v[0]));
		case ImagePlus.COLOR_256:
			return(", index=" + v[3] + ", value=" + v[0] + "," + v[1] + "," + v[2]);
		case ImagePlus.COLOR_RGB:
			return(", value=" + v[0] + "," + v[1] + "," + v[2]);
		default:
			return("");
	}
} /* end getValueAsString */

/*------------------------------------------------------------------*/
private void setControl (
) {
	switch (tb.getCurrentTool()) {
		case ADD_CIRCLE:
			imp.getWindow().getCanvas().setCursor(crosshairCursor);
			break;
		case FILE:
		case MAGNIFIER:
		case MOVE_CIRCLE:
			imp.getWindow().getCanvas().setCursor(defaultCursor);
			break;
	}
} /* end setControl */

} /* end class ParticleAction */

/*====================================================================
|	ParticleHandler
\===================================================================*/

/*********************************************************************
 * This class is responsible for dealing with the list of point
 * coordinates and for their visual appearance.
 ********************************************************************/
class ParticleHandler
	extends
		Roi

{ /* begin class ParticleHandler */


private int CIRCLE_HALFSIZE = 2;
private final Vector listPoints = new Vector(512, 512);
private ImagePlus imp;
private ParticleAction pa;
private ParticleToolbar tb;
private int currentPoint = -1;
private int numPoints = 0;
private boolean started = false;
private int fp =0;
private int fn =0;

/*....................................................................
	Public methods
....................................................................*/

/*********************************************************************
 * This method adds a new point to the list, with a color that is as
 * different as possible from all those that are already in use. The
 * points are stored in pixel units rather than canvas units to cope
 * for different zooming factors.
 *
 * @param x Horizontal coordinate, in canvas units.
 * @param y Vertical coordinate, in canvas units.
 ********************************************************************/
public void addPoint (
	final int x,
	final int y
) {
	final Point p = new Point(x, y);
	listPoints.addElement(p);
	currentPoint = numPoints;
	numPoints++;
} /* end addPoint */

/*********************************************************************
 * Restore the listeners
 ********************************************************************/
public void cleanUp (
) {
	removePoints();
	final ImageCanvas ic = imp.getWindow().getCanvas();
	ic.removeKeyListener(pa);
	ic.removeMouseListener(pa);
	ic.removeMouseMotionListener(pa);
	ic.addMouseMotionListener(ic);
	ic.addMouseListener(ic);
	ic.addKeyListener(IJ.getInstance());
} /* end cleanUp */

/*********************************************************************
 * Draw the landmarks and outline the current point if there is one.
 *
 * @param g Graphics environment.
 ********************************************************************/
public void draw (
	final Graphics g
) {
	if (started) {
		final float mag = (float)ic.getMagnification();
		Calibration cal = imp.getCalibration();
		double oldScale = cal.pixelWidth!=0?1.0/cal.pixelWidth:0;
		//CIRCLE_HALFSIZE = (int) oldScale*2/137;
		CIRCLE_HALFSIZE = 5;
		int radius;
		final int dx = (int)(mag / 2.0);
		final int dy = (int)(mag / 2.0);
		for (int k = 0; (k < listPoints.size()); k++) {
			final Point p = (Point)listPoints.elementAt(k);
			radius = ic.screenX(p.x+CIRCLE_HALFSIZE)-ic.screenX(p.x-CIRCLE_HALFSIZE);
			g.setColor(Color.red);
			if (k == currentPoint) {
				if (pa.isFrontmost()) {
					g.drawLine(ic.screenX(p.x - CIRCLE_HALFSIZE) + dx,
					ic.screenY(p.y) + dy,
					ic.screenX(p.x + CIRCLE_HALFSIZE) + dx,
					ic.screenY(p.y) + dy);
				g.drawLine(ic.screenX(p.x) + dx,
					ic.screenY(p.y - CIRCLE_HALFSIZE) + dy,
					ic.screenX(p.x) + dx,
					ic.screenY(p.y + CIRCLE_HALFSIZE) + dy);
				g.drawOval(ic.screenX(p.x-CIRCLE_HALFSIZE) + dx,
					ic.screenY(p.y-CIRCLE_HALFSIZE) + dy,
					radius,radius);
				}
				else {
					g.drawLine(ic.screenX(p.x - CIRCLE_HALFSIZE + 1) + dx,
						ic.screenY(p.y - CIRCLE_HALFSIZE + 1) + dy,
						ic.screenX(p.x + CIRCLE_HALFSIZE - 1) + dx,
						ic.screenY(p.y + CIRCLE_HALFSIZE - 1) + dy);
					g.drawLine(ic.screenX(p.x - CIRCLE_HALFSIZE + 1) + dx,
						ic.screenY(p.y + CIRCLE_HALFSIZE - 1) + dy,
						ic.screenX(p.x + CIRCLE_HALFSIZE - 1) + dx,
						ic.screenY(p.y - CIRCLE_HALFSIZE + 1) + dy);
					g.drawOval(ic.screenX(p.x-CIRCLE_HALFSIZE) + dx,
						ic.screenY(p.y-CIRCLE_HALFSIZE) + dy,
						radius,radius);
				}
			}
			else {
					g.drawOval(ic.screenX(p.x-CIRCLE_HALFSIZE) + dx,
					ic.screenY(p.y-CIRCLE_HALFSIZE) + dy,
					radius,radius);
			}
		}
		if (updateFullWindow) {
			updateFullWindow = false;
			imp.draw();
		}
	}
} /* end draw */

/*********************************************************************
 * Let the point that is closest to the given coordinates become the
 * current landmark.
 *
 * @param x Horizontal coordinate, in canvas units.
 * @param y Vertical coordinate, in canvas units.
 ********************************************************************/
public void findClosest (
	int x,
	int y
) {
	if (listPoints.size() == 0) {
		currentPoint = -1;
		return;
	}
	x = ic.offScreenX(x);
	y = ic.offScreenY(y);
	Point p = new Point((Point)listPoints.elementAt(currentPoint));
	float distance = (float)(x - p.x) * (float)(x - p.x)
		+ (float)(y - p.y) * (float)(y - p.y);
	for (int k = 0; (k < listPoints.size()); k++) {
		p = (Point)listPoints.elementAt(k);
		final float candidate = (float)(x - p.x) * (float)(x - p.x)
			+ (float)(y - p.y) * (float)(y - p.y);
		if (candidate < distance) {
			distance = candidate;
			currentPoint = k;
		}
	}
} /* end findClosest */

/*********************************************************************
 * Return the current point as a <code>Point</code> object.
 ********************************************************************/
public Point getPoint (
) {
	return((0 <= currentPoint) ? (Point)listPoints.elementAt(currentPoint) : (null));
} /* end getPoint */

/*********************************************************************
 * Return the list of points.
 ********************************************************************/
public Vector getPoints (
) {
	return(listPoints);
} /* end getPoints */

public int getNum(){
	return numPoints;
}

public int getFP(){
	return fp;
}

public int getFN(){
	return fn;
}

public void FPone(){
	fp++;
}

public void FNone(){
	fn++;
}

/*********************************************************************
 * Modify the location of the current point. Clip the admissible range
 * to the image size.
 *
 * @param x Desired new horizontal coordinate in canvas units.
 * @param y Desired new vertical coordinate in canvas units.
 ********************************************************************/
public void movePoint (
	int x,
	int y
) {
	if (0 <= currentPoint) {
		x = ic.offScreenX(x);
		y = ic.offScreenY(y);
		x = (x < 0) ? (0) : (x);
		x = (imp.getWidth() <= x) ? (imp.getWidth() - 1) : (x);
		y = (y < 0) ? (0) : (y);
		y = (imp.getHeight() <= y) ? (imp.getHeight() - 1) : (y);
		listPoints.removeElementAt(currentPoint);
		final Point p = new Point(x, y);
		listPoints.insertElementAt(p, currentPoint);
	}
} /* end movePoint */

/*********************************************************************
 * Change the current point.
 ********************************************************************/
public void nextPoint (
) {
	currentPoint = (currentPoint == (numPoints - 1)) ? (0) : (currentPoint + 1);
} /* end nextPoint */

/*********************************************************************
 * This constructor stores a local copy of its parameters and initializes
 * the current spectrum. It also creates the object that takes care of
 * the interactive work.
 *
 * @param imp <code>ImagePlus<code> object where points are being picked.
 * @param tb <code>pointToolbar<code> object that handles the toolbar.
 ********************************************************************/
public ParticleHandler (
	final ImagePlus imp,
	final ParticleToolbar tb
) {
	super(0, 0, imp.getWidth(), imp.getHeight(), imp);
	this.imp = imp;
	this.tb = tb;
	pa = new ParticleAction(imp, this, tb);
	final ImageCanvas ic = imp.getWindow().getCanvas();
	ic.removeKeyListener(IJ.getInstance());
	ic.removeMouseListener(ic);
	ic.removeMouseMotionListener(ic);
	ic.addMouseMotionListener(pa);
	ic.addMouseListener(pa);
	ic.addKeyListener(pa);
	started = true;
} /* end ParticleHandler */

/*********************************************************************
 * Remove the current point. Make its color available again.
 ********************************************************************/
public void removePoint (
) {
	if (0 < listPoints.size()) {
		listPoints.removeElementAt(currentPoint);
		numPoints--;
	}
	currentPoint = numPoints - 1;
	if (currentPoint < 0) {
		tb.setTool(pa.ADD_CIRCLE);
	}
} /* end removePoint */

/*********************************************************************
 * Remove all points and make every color available.
 ********************************************************************/
public void removePoints (
) {
	listPoints.removeAllElements();
	numPoints = 0;
	currentPoint = -1;
	tb.setTool(pa.ADD_CIRCLE);
	imp.setRoi(this);
} /* end removePoints */

} /* end class ParticleHandler */


/*====================================================================
|	ParticlePickerClearAll
\===================================================================*/

/*********************************************************************
 * This class creates a dialog to remove every point.
 ********************************************************************/
class ParticlePickerClearAll
	extends
		Dialog
	implements
		ActionListener

{ /* begin class ParticlePickerClearAll */

/*....................................................................
	Private variables
....................................................................*/
private ParticleHandler ph;

/*....................................................................
	Public methods
....................................................................*/

/*********************************************************************
 * This method processes the button actions.
 *
 * @param ae The expected actions are as follows:
 * <ul><li><code>Clear All</code>: Remove everything;</li>
 * <li><code>Cancel</code>: Do nothing.</li></ul>
 ********************************************************************/
public void actionPerformed (
	final ActionEvent ae
) {
	if (ae.getActionCommand().equals("Clear All")) {
		ph.removePoints();
		setVisible(false);
	}
	else if (ae.getActionCommand().equals("Cancel")) {
		setVisible(false);
	}
} /* end actionPerformed */

/*********************************************************************
 * Return some additional margin to the dialog, for aesthetic purposes.
 * Necessary for the current MacOS X Java version, lest the first item
 * disappears from the frame.
 ********************************************************************/
public Insets getInsets (
) {
	return(new Insets(0, 20, 20, 20));
} /* end getInsets */

/*********************************************************************
 * This constructor stores a local copy of its parameters and prepares
 * the layout of the dialog.
 *
 * @param parentWindow Parent window.
 * @param ph <code>pointHandler<code> object that handles operations.
 ********************************************************************/
ParticlePickerClearAll (
	final Frame parentWindow,
	final ParticleHandler ph
) {
	super(parentWindow, "Removing Points", true);
	this.ph = ph;
	setLayout(new GridLayout(0, 1));
	final Button removeButton = new Button("Clear All");
	removeButton.addActionListener(this);
	final Button cancelButton = new Button("Cancel");
	cancelButton.addActionListener(this);
	final Label separation1 = new Label("");
	final Label separation2 = new Label("");
	add(separation1);
	add(removeButton);
	add(separation2);
	add(cancelButton);
	pack();
} /* end ParticlePickerClearAll */

} /* end class ParticlePickerClearAll */


/*====================================================================
|	ParticlePickerFile
\===================================================================*/

/*********************************************************************
 * This class creates a dialog to store and retrieve points into and
 * from a text file, respectively.
 ********************************************************************/
class ParticlePickerFile
	extends
		Dialog
	implements
		ActionListener

{ /* begin class ParticlePickerFile */

/*....................................................................
	Private variables
....................................................................*/
private final CheckboxGroup choice = new CheckboxGroup();
private ImagePlus imp;
private ParticleHandler ph;

/*....................................................................
	Public methods
....................................................................*/

/*********************************************************************
 * This method processes the button actions.
 *
 * @param ae The expected actions are as follows:
 * <ul><li><code>Save as</code>: Save points into a text file;</li>
 * <li><code>Show</code>: Display the coordinates in ImageJ's window;</li>
 * <li><code>Open</code>: Retrieve points from a text file;</li>
 * <li><code>Cancel</code>: Do nothing.</li></ul>
 ********************************************************************/
public void actionPerformed (
	final ActionEvent ae
) {
	this.setVisible(false);
	if (ae.getActionCommand().equals("Save as")) {
		final Frame f = new Frame();
		final FileDialog fd = new FileDialog(f, "Point list", FileDialog.SAVE);
		final String path;
		String filename = imp.getTitle();
		final int dot = filename.lastIndexOf('.');
		if (dot == -1) {
			fd.setFile(filename + ".txt");
		}
		else {
			filename = filename.substring(0, dot);
			fd.setFile(filename + ".txt");
		}
		fd.setVisible(true);
		path = fd.getDirectory();
		filename = fd.getFile();
		if ((path == null) || (filename == null)) {
			return;
		}
		try {
			final FileWriter fw = new FileWriter(path + filename);
			final Vector list = ph.getPoints();
			Point p;
			String n;
			String x;
			String y;
			fw.write("point     x     y\n");
			for (int k = 0; (k < list.size()); k++) {
				n = "" + k;
				while (n.length() < 5) {
					n = " " + n;
				}
				p = (Point)list.elementAt(k);
				x = "" + p.x;
				while (x.length() < 5) {
					x = " " + x;
				}
				y = "" + p.y;
				while (y.length() < 5) {
					y = " " + y;
				}
				fw.write(n + " " + x + " " + y + "\n");
			}
			fw.close();
		} catch (IOException e) {
			IJ.error("IOException exception");
		} catch (SecurityException e) {
			IJ.error("Security exception");
		}
	}
	else if (ae.getActionCommand().equals("Show")) {
		final Vector list = ph.getPoints();
		Point p;
		String n;
		String x;
		String y;
		IJ.getTextPanel().setFont(new Font("Monospaced", Font.PLAIN, 12));
		IJ.setColumnHeadings(" point\t      x\t      y\t");
		for (int k = 0; (k < list.size()); k++) {
			n = "" + k;
			while (n.length() < 6) {
				n = " " + n;
			}
			p = (Point)list.elementAt(k);
			x = "" + p.x;
			while (x.length() < 7) {
				x = " " + x;
			}
			y = "" + p.y;
			while (y.length() < 7) {
				y = " " + y;
			}
			IJ.write(n + "\t" + x + "\t" + y + "\t");
		}

		//IJ.write("fasle positive : "+ph.getFP()+"\nfalse negtive : "+ph.getFN()+"\n");
	}
	else if (ae.getActionCommand().equals("Open")) {
		final Frame f = new Frame();
		final FileDialog fd = new FileDialog(f, "Point list", FileDialog.LOAD);
		fd.setVisible(true);
		final String path = fd.getDirectory();
		final String filename = fd.getFile();
		if ((path == null) || (filename == null)) {
			return;
		}
		try {
			final FileReader fr = new FileReader(path + filename);
			final BufferedReader br = new BufferedReader(fr);
			ph.removePoints();
			String line;
			String pString;
			String xString;
			String yString;
			int separatorIndex;
			int x;
			int y;
			if ((line = br.readLine()) == null) {
				fr.close();
				return;
			}
			while ((line = br.readLine()) != null) {
				line = line.trim();
				separatorIndex = line.indexOf(' ');
				if (separatorIndex == -1) {
					fr.close();
					IJ.error("Invalid file");
					return;
				}
				line = line.substring(separatorIndex);
				line = line.trim();
				separatorIndex = line.indexOf(' ');
				if (separatorIndex == -1) {
					fr.close();
					IJ.error("Invalid file");
					return;
				}
				xString = line.substring(0, separatorIndex);
				xString = xString.trim();
				line = line.substring(separatorIndex);
				line = line.trim();
				separatorIndex = line.indexOf(' ');
				if (separatorIndex == -1) {
					separatorIndex = line.length();
				}
				yString = line.substring(0, separatorIndex);
				yString = yString.trim();
				x = Integer.parseInt(xString);
				y = Integer.parseInt(yString);
				ph.addPoint(x, y);
			}
			fr.close();
		} catch (FileNotFoundException e) {
			IJ.error("File not found exception");
		} catch (IOException e) {
			IJ.error("IOException exception");
		} catch (NumberFormatException e) {
			IJ.error("Number format exception");
		}
	}
	else if (ae.getActionCommand().equals("Cancel")) {
	}
} /* end actionPerformed */

/*********************************************************************
 * Return some additional margin to the dialog, for aesthetic purposes.
 * Necessary for the current MacOS X Java version, lest the first item
 * disappears from the frame.
 ********************************************************************/
public Insets getInsets (
) {
	return(new Insets(0, 20, 20, 20));
} /* end getInsets */

/*********************************************************************
 * This constructor stores a local copy of its parameters and prepares
 * the layout of the dialog.
 *
 * @param parentWindow Parent window.
 * @param ph <code>pointHandler<code> object that handles operations.
 * @param imp <code>ImagePlus<code> object where points are being picked.
 ********************************************************************/
ParticlePickerFile (
	final Frame parentWindow,
	final ParticleHandler ph,
	final ImagePlus imp
) {
	super(parentWindow, "Point List", true);
	this.ph = ph;
	this.imp = imp;
	setLayout(new GridLayout(0, 1));
	final Button saveAsButton = new Button("Save as");
	final Button showButton = new Button("Show");
	final Button openButton = new Button("Open");
	final Button cancelButton = new Button("Cancel");
	saveAsButton.addActionListener(this);
	showButton.addActionListener(this);
	openButton.addActionListener(this);
	cancelButton.addActionListener(this);
	final Label separation1 = new Label("");
	final Label separation2 = new Label("");
	add(separation1);
	add(saveAsButton);
	add(showButton);
	add(openButton);
	add(separation2);
	add(cancelButton);
	pack();
} /* end ParticlePickerFile */

} /* end class ParticlePickerFile */

/*====================================================================
|	ParticlePickerTerminate
\===================================================================*/

/*********************************************************************
 * This class creates a dialog to return to ImageJ.
 ********************************************************************/
class ParticlePickerTerminate
	extends
		Dialog
	implements
		ActionListener

{ /* begin class ParticlePickerTerminate */

/*....................................................................
	Private variables
....................................................................*/
private final CheckboxGroup choice = new CheckboxGroup();
private boolean cancel = false;

/*....................................................................
	Public methods
....................................................................*/

/*********************************************************************
 * This method processes the button actions.
 *
 * @param ae The expected actions are as follows:
 * <ul><li><code>Done</code>: Return to ImageJ;</li>
 * <li><code>Cancel</code>: Do nothing.</li></ul>
 ********************************************************************/
public void actionPerformed (
	final ActionEvent ae
) {
	this.setVisible(false);
	if (ae.getActionCommand().equals("Done")) {
	}
	else if (ae.getActionCommand().equals("Cancel")) {
		cancel = true;
	}
} /* end actionPerformed */

/*********************************************************************
 * Return <code>true</code> only if the user chose <code>Cancel</code>.
 ********************************************************************/
public boolean choseCancel (
) {
	return(cancel);
} /* end choseCancel */

/*********************************************************************
 * Return some additional margin to the dialog, for aesthetic purposes.
 * Necessary for the current MacOS X Java version, lest the first item
 * disappears from the frame.
 ********************************************************************/
public Insets getInsets (
) {
	return(new Insets(0, 40, 20, 40));
} /* end getInsets */

/*********************************************************************
 * This constructor prepares the layout of the dialog.
 *
 * @param parentWindow Parent window.
 ********************************************************************/
ParticlePickerTerminate (
	final Frame parentWindow
) {
	super(parentWindow, "Back to ImageJ", true);
	setLayout(new GridLayout(0, 1));
	final Button doneButton = new Button("Done");
	final Button cancelButton = new Button("Cancel");
	doneButton.addActionListener(this);
	cancelButton.addActionListener(this);
	final Label separation1 = new Label("");
	final Label separation2 = new Label("");
	add(separation1);
	add(doneButton);
	add(separation2);
	add(cancelButton);
	pack();
} /* end ParticlePickerTerminate */

} /* end class ParticlePickerTerminate */


class ParticleFinder extends ParticleAnalyzer {

	boolean error;
	Vector listPoints = new Vector(512,512);
	int numPoints = 0;


	public void run2(ImagePlus imp, ImageProcessor ip) {

		error = !analyze(imp, ip);
	}

	public void addPoint(final int x, final int y){
			final Point p = new Point(x,y);
			listPoints.addElement(p);
			numPoints++;
	}/* end addPoint */


	public void showPoints(){
		int i;
		Point p;
		String n;
		String x;
		String y;
		String z = ""+imp.getCurrentSlice();
		while(z.length()<7){
			z=" "+z;
		}
		Calibration cal = imp.getCalibration();
		IJ.setColumnHeadings(" point\t      x\t      y\t  slice");
		IJ.write(imp.getWidth()*cal.pixelWidth + " " + imp.getHeight()*cal.pixelHeight +"\n");
		for (i=0;i<listPoints.size();i++){
			n = ""+i;
			while(n.length()<6){
				n = " "+n;
			}
			p = (Point)listPoints.elementAt(i);
			x = ""+p.x;
			while(x.length()<7){
				x = " "+x;
			}
			y = ""+p.y;
			while(y.length()<7){
				y = " "+y;
			}
			IJ.write(n + "\t" + x + "\t" + y + "\t" + z);
		}
	} /* end showResults */

	protected void saveResults(ImageStatistics stats, Roi roi) {
		super.saveResults(stats, roi);
		Calibration cal = imp.getCalibration();
		addPoint((int)(stats.xCenterOfMass/cal.pixelWidth),(int)(stats.yCenterOfMass/cal.pixelHeight));
	}
}


/*====================================================================
|	ParticleToolbar
\===================================================================*/

/*********************************************************************
 * This class deals with the toolbar that gets substituted to that of
 * ImageJ.
 ********************************************************************/
class ParticleToolbar
	extends
		Canvas
	implements
		MouseListener

{ /* begin class ParticleToolbar */

/*....................................................................
	Private variables
....................................................................*/
private static final int NUM_TOOLS = 19;
private static final int SIZE = 22;
private static final int OFFSET = 3;
private static final Color gray = Color.lightGray;
private static final Color brighter = gray.brighter();
private static final Color darker = gray.darker();
private static final Color evenDarker = darker.darker();
private final boolean[] down = new boolean[NUM_TOOLS];
private Graphics g;
private ImagePlus imp;
private Toolbar previousInstance;
private ParticleHandler ph;
private ParticleToolbar instance;
private long mouseDownTime;
private int currentTool = ParticleAction.ADD_CIRCLE;
private int x;
private int y;
private int xOffset;
private int yOffset;

/*....................................................................
	Public methods
....................................................................*/

/*********************************************************************
 * Return the index of the tool that is currently activated.
 ********************************************************************/
public int getCurrentTool (
) {
	return(currentTool);
} /* getCurrentTool */

/*********************************************************************
 * Listen to <code>mouseClicked</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void mouseClicked (
	final MouseEvent e
) {
} /* end mouseClicked */

/*********************************************************************
 * Listen to <code>mouseEntered</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void mouseEntered (
	final MouseEvent e
) {
} /* end mouseEntered */

/*********************************************************************
 * Listen to <code>mouseExited</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void mouseExited (
	final MouseEvent e
) {
} /* end mouseExited */

/*********************************************************************
 * Listen to <code>mousePressed</code> events. Test for single or double
 * clicks and perform the relevant action.
 *
 * @param e Event.
 ********************************************************************/
public void mousePressed (
	final MouseEvent e
) {
	final int x = e.getX();
	final int y = e.getY();
	final int previousTool = currentTool;
	int newTool = 0;
	for (int i = 0; (i < NUM_TOOLS); i++) {
		if (((i * SIZE) < x) && (x < (i * SIZE + SIZE))) {
			newTool = i;
		}
	}
	final boolean doubleClick = ((newTool == getCurrentTool())
		&& ((System.currentTimeMillis() - mouseDownTime) <= 500L));
	mouseDownTime = System.currentTimeMillis();
	setTool(newTool);
	if (doubleClick) {
		switch (newTool) {
			case ParticleAction.ADD_CIRCLE:
				ParticlePickerClearAll clearAllDialog
					= new ParticlePickerClearAll(IJ.getInstance(), ph);
				GUI.center(clearAllDialog);
				clearAllDialog.setVisible(true);
				clearAllDialog.dispose();
				break;
		}
	}
	switch (newTool) {
		case ParticleAction.FILE:
			ParticlePickerFile fileDialog
				= new ParticlePickerFile(IJ.getInstance(), ph, imp);
			GUI.center(fileDialog);
			fileDialog.setVisible(true);
			setTool(previousTool);
			fileDialog.dispose();
			break;
		case ParticleAction.TERMINATE:
			ParticlePickerTerminate terminateDialog
				= new ParticlePickerTerminate(IJ.getInstance());
			GUI.center(terminateDialog);
			terminateDialog.setVisible(true);
			if (terminateDialog.choseCancel()) {
				setTool(previousTool);
			}
			else {
				ph.cleanUp();
				restorePreviousToolbar();
				Toolbar.getInstance().repaint();
			}
			terminateDialog.dispose();
			break;
		case ParticleAction.FINDER:
			ParticleFinder pf = new ParticleFinder();
			int code = pf.setup("",this.imp);
			ImageProcessor ip = imp.getProcessor();
			pf.run2(this.imp,ip);
			for (int k=0;k<pf.numPoints;k++){
				Point p = (Point) pf.listPoints.elementAt(k);
				ph.addPoint(p.x,p.y);
			}
			break;
		case ParticleAction.CLUSTER:
		    double ddd=105;
			GenericDialog dia = new GenericDialog("Set Threshold", IJ.getInstance());
			dia.addNumericField("New Threshold", ddd, 0);
			dia.showDialog();

			if(dia.wasCanceled()) return;
			if(dia.invalidNumber()) {
				IJ.showMessage("Error", "Invalid input Number");
				return;
			}

			ddd = dia.getNextNumber();

		    ImageProcessor myip = imp.getProcessor();
		    ImageProcessor ip2 = myip.duplicate();
		    ClusterFile cfileDialog = new ClusterFile(IJ.getInstance(),ip2,ph);
		    GUI.center(cfileDialog);
		    cfileDialog.setThreshold(ddd);
		    cfileDialog.setVisible(true);
		    cfileDialog.dispose();
		    break;

	}
} /* mousePressed */

/*********************************************************************
 * Listen to <code>mouseReleased</code> events.
 *
 * @param e Ignored.
 ********************************************************************/
public void mouseReleased (
	final MouseEvent e
) {
} /* end mouseReleased */

/*********************************************************************
 * Draw the tools of the toolbar.
 *
 * @param g Graphics environment.
 ********************************************************************/
public void paint (
	final Graphics g
) {
	for (int i = 0; (i < NUM_TOOLS); i++) {
		drawButton(g, i);
	}
} /* paint */

/*********************************************************************
 * This constructor substitutes ImageJ's toolbar by that of ParticlePicker_.
 *
 * @param previousToolbar ImageJ's toolbar.
 ********************************************************************/
public ParticleToolbar (
	final Toolbar previousToolbar
) {
	previousInstance = previousToolbar;
	instance = this;
	final Container container = previousToolbar.getParent();
	final Component component[] = container.getComponents();
	for (int i = 0; (i < component.length); i++) {
		if (component[i] == previousToolbar) {
			container.remove(previousToolbar);
			container.add(this, i);
			break;
		}
	}
	resetButtons();
	down[currentTool] = true;
	setTool(currentTool);
	setForeground(evenDarker);
	setBackground(gray);
	addMouseListener(this);
	container.validate();
} /* end ParticleToolbar */

/*********************************************************************
 * Setup the Particle handler.
 *
 * @param ph <code>ParticleHandler<code> object that handles operations.
 * @param imp <code>ImagePlus<code> object where points are being picked.
 ********************************************************************/
public void setWindow (
	final ParticleHandler ph,
	final ImagePlus imp
) {
	this.ph = ph;
	this.imp = imp;
} /* end setWindow */

/*********************************************************************
 * Setup the current tool. The selection of non-functional tools is
 * honored but leads to a no-op action.
 *
 * @param tool Admissible tools belong to [<code>0</code>,
 * <code>NUM_TOOLS - 1</code>]
 ********************************************************************/
public void setTool (
	final int tool
) {
	if (tool == currentTool) {
		return;
	}
	down[tool] = true;
	down[currentTool] = false;
	final Graphics g = this.getGraphics();
	drawButton(g, currentTool);
	drawButton(g, tool);
	g.dispose();
	showMessage(tool);
	currentTool = tool;
} /* end setTool */

/*....................................................................
	Private methods
....................................................................*/

 /*------------------------------------------------------------------*/
private void d (
	int x,
	int y
) {
	x += xOffset;
	y += yOffset;
	g.drawLine(this.x, this.y, x, y);
	this.x = x;
	this.y = y;
} /* end d */

/*------------------------------------------------------------------*/
private void drawButton (
	final Graphics g,
	final int tool
) {
	fill3DRect(g, tool * SIZE + 1, 1, SIZE, SIZE - 1, !down[tool]);
	g.setColor(Color.black);
	int x = tool * SIZE + OFFSET;
	int y = OFFSET;
	if (down[tool]) {
		x++;
		y++;
	}
	this.g = g;
	switch (tool) {
		case ParticleAction.ADD_CIRCLE:
			xOffset = x;
			yOffset = y;
			m(7, 0);
			d(7, 1);
			m(6, 2);
			d(6, 3);
			m(8, 2);
			d(8, 3);
			m(5, 4);
			d(5, 5);
			m(9, 4);
			d(9, 5);
			m(4, 6);
			d(4, 8);
			m(10, 6);
			d(10, 8);
			m(5, 9);
			d(5, 14);
			m(9, 9);
			d(9, 14);
			m(7, 4);
			d(7, 6);
			m(7, 8);
			d(7, 8);
			m(4, 11);
			d(10, 11);
			g.fillRect(x + 6, y + 12, 3, 3);
			break;
		case ParticleAction.MOVE_CIRCLE:
			xOffset = x;
			yOffset = y;
			m(1, 1);
			d(1, 10);
			m(2, 2);
			d(2, 9);
			m(3, 3);
			d(3, 8);
			m(4, 4);
			d(4, 7);
			m(5, 5);
			d(5, 7);
			m(6, 6);
			d(6, 7);
			m(7, 7);
			d(7, 7);
			m(11, 5);
			d(11, 6);
			m(10, 7);
			d(10, 8);
			m(12, 7);
			d(12, 8);
			m(9, 9);
			d(9, 11);
			m(13, 9);
			d(13, 11);
			m(10, 12);
			d(10, 15);
			m(12, 12);
			d(12, 15);
			m(11, 9);
			d(11, 10);
			m(11, 13);
			d(11, 15);
			m(9, 13);
			d(13, 13);
			break;
		case ParticleAction.FILE:
			xOffset = x;
			yOffset = y;
			m(3, 1);
			d(9, 1);
			d(9, 4);
			d(12, 4);
			d(12, 14);
			d(3, 14);
			d(3, 1);
			m(10, 2);
			d(11, 3);
			m(5, 4);
			d(7, 4);
			m(5, 6);
			d(10, 6);
			m(5, 8);
			d(10, 8);
			m(5, 10);
			d(10, 10);
			m(5, 12);
			d(10, 12);
			break;
		case ParticleAction.TERMINATE:
			xOffset = x;
			yOffset = y;
			m(5, 0);
			d(5, 8);
			m(4, 5);
			d(4, 7);
			m(3, 6);
			d(3, 7);
			m(2, 7);
			d(2, 9);
			m(1, 8);
			d(1, 9);
			m(2, 10);
			d(6, 10);
			m(3, 11);
			d(3, 13);
			m(1, 14);
			d(6, 14);
			m(0, 15);
			d(7, 15);
			m(2, 13);
			d(2, 13);
			m(5, 13);
			d(5, 13);
			m(7, 8);
			d(14, 8);
			m(8, 7);
			d(15, 7);
			m(8, 9);
			d(13, 9);
			m(9, 6);
			d(9, 10);
			m(15, 4);
			d(15, 6);
			d(14, 6);
			break;
		case ParticleAction.MAGNIFIER:
			xOffset = x + 2;
			yOffset = y + 2;
			m(3, 0);
			d(3, 0);
			d(5, 0);
			d(8, 3);
			d(8, 5);
			d(7, 6);
			d(7, 7);
			d(6, 7);
			d(5, 8);
			d(3, 8);
			d(0, 5);
			d(0, 3);
			d(3, 0);
			m(8, 8);
			d(9, 8);
			d(13, 12);
			d(13, 13);
			d(12, 13);
			d(8, 9);
			d(8, 8);
			break;
		case ParticleAction.FINDER:
			xOffset = x + 4;
			yOffset = y + 4;
			m(3, 0);
			d(3, 0);
			d(5, 0);
			d(8, 3);
			d(8, 5);
			d(7, 6);
			d(7, 7);
			d(6, 7);
			d(5, 8);
			d(3, 8);
			d(0, 5);
			d(0, 3);
			d(3, 0);
			break;
		case ParticleAction.CLUSTER:
			xOffset = x;
			yOffset = y;
			m(3, 0);
			d(4, 0);
			d(4,2);
			d(3, 2);
			d(3, 1);
			m(8, 3);
			d(9, 3);
			d(9, 5);
			d(8, 5);
			d(8, 4);
			m(5, 10);
			d(6, 10);
			d(6, 12);
			d(5, 12);
			d(5, 11);
			m(12, 10);
			d(13, 10);
			d(13, 12);
			d(12, 12);
			d(12, 11);

			break;

	   case ParticleAction.HAND:
	        xOffset = x+1; yOffset = y+1;
		    m(5,14); d(2,11); d(2,10); d(0,8); d(0,7); d(1,6); d(2,6); d(4,8);
		    d(4,6); d(3,5); d(3,4); d(2,3); d(2,2); d(3,1); d(4,1); d(5,2); d(5,3);
		    m(6,5); d(6,1); d(7,0); d(8,0); d(9,1); d(9,5);
		    m(9,1); d(11,1); d(12,2); d(12,6);
		    m(13,4); d(14,3); d(15,4); d(15,7); d(14,8);
		    d(14,10); d(13,11); d(13,12); d(12,13); d(12,14);
            break;
	}
} /* end drawButton */

/*------------------------------------------------------------------*/
private void fill3DRect (
	final Graphics g,
	final int x,
	final int y,
	final int width,
	final int height,
	final boolean raised
) {
	if (raised) {
		g.setColor(gray);
	}
	else {
		g.setColor(darker);
	}
	g.fillRect(x + 1, y + 1, width - 2, height - 2);
	g.setColor((raised) ? (brighter) : (evenDarker));
	g.drawLine(x, y, x, y + height - 1);
	g.drawLine(x + 1, y, x + width - 2, y);
	g.setColor((raised) ? (evenDarker) : (brighter));
	g.drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1);
	g.drawLine(x + width - 1, y, x + width - 1, y + height - 2);
} /* end fill3DRect */

/*------------------------------------------------------------------*/
private void m (
	final int x,
	final int y
) {
	this.x = xOffset + x;
	this.y = yOffset + y;
} /* end m */

/*------------------------------------------------------------------*/
private void resetButtons (
) {
	for (int i = 0; (i < NUM_TOOLS); i++) {
		down[i] = false;
	}
} /* end resetButtons */

/*------------------------------------------------------------------*/
private void restorePreviousToolbar (
) {
	final Container container = instance.getParent();
	final Component component[] = container.getComponents();
	for (int i = 0; (i < component.length); i++) {
		if (component[i] == instance) {
			container.remove(instance);
			container.add(previousInstance, i);
			container.validate();
			break;
		}
	}
} /* end restorePreviousToolbar */

/*------------------------------------------------------------------*/
private void showMessage (
	final int tool
) {
	switch (tool) {
		case ParticleAction.ADD_CIRCLE:
			IJ.showStatus("Add circles");
			return;
		case ParticleAction.MOVE_CIRCLE:
			IJ.showStatus("Move circles");
			return;
		case ParticleAction.FILE:
			IJ.showStatus("Export/Import list of points");
			return;
		case ParticleAction.TERMINATE:
			IJ.showStatus("Exit PointPicker");
			return;
		case ParticleAction.MAGNIFIER:
			IJ.showStatus("Magnifying glass");
			return;
		case ParticleAction.FINDER:
			IJ.showStatus("Find particles");
			return;
		case ParticleAction.CLUSTER:
		    IJ.showStatus("Find Clusters");
		    return;
		default:
			IJ.showStatus("Undefined operation");
			return;
	}
} /* end showMessage */

} /* end class ParticleToolbar */

class Particle {
	public int x;
	public int y;
	public int cnum;
	public int nn;
	public Vector neighbors = new Vector(0,16);

	Particle(int a,int b, int c){
		x=a;
		y=b;
		cnum=c;
		nn=0;
	}

	public void addNeighbor(Particle p){
		neighbors.addElement(p);
		nn++;
	}

	public void print(){
		IJ.write("X= "+x+", Y= "+y+"Cluster #:"+cnum+"\n");
	}
}

class ParticleCluster {
	private Vector particles = new Vector(0,16);
	private Vector PCNeighbors = new Vector(0,16);
	private int num_p =0;
	private int neigb= 0;
	public int max_x = 0;
	public int max_y = 0;
	public int min_x = 0;
	public int min_y = 0;
	private int cluster_id = 0;

	public void addParticle(Particle p){

		if(num_p==0) {
			max_x=p.x;
			min_x=p.x;
			max_y=p.y;
			min_y=p.y;
		}

		p.cnum=cluster_id;
		particles.addElement(p);
		addNeighbor(p);
		num_p++;
		if(p.x>max_x) max_x=p.x;
		if(p.y>max_y) max_y=p.y;
		if(p.x<min_x) min_x=p.x;
		if(p.y<min_y) min_y=p.y;
	}

	public double distance(Particle p,int x, int y){
		double d1;
		d1 = (p.x-x)*(p.x-x) + (p.y-y)*(p.y-y);
		return Math.pow((double)d1,0.5);
	}

	public double getMinD(int x,int y){
		if(num_p==0) return -1.0;

		double result,temp;
		Particle p1,p;

		p1=(Particle) particles.elementAt(0);
		result = distance(p1,x,y);

		for(int i=1;i<num_p;i++){
			p=(Particle) particles.elementAt(i);
			temp = distance(p,x,y);
			if(temp<result) result=temp;
		}

		return result;
	}



	public void setID(int i){
		cluster_id=i;
	}

	public int getNum(){
		return num_p;
	}

	public void print(){
		IJ.write("Cluster #:"+cluster_id+" Number of particles :"+num_p+"\n");
		IJ.write(" x from "+min_x+" to "+max_x+" y from "+min_y+" to "+max_y+"\n");
	}

	ParticleCluster (int id){
		cluster_id=id;
	}


	private void addNeighbor(Particle p){
		Particle p1;
		for(int i=0;i<p.nn;i++){
			p1=(Particle) p.neighbors.elementAt(i);
			if(!InCluster(p1)) {
				PCNeighbors.addElement(p1);
				neigb++;
			}
		}
	}

	private int CloseNeighbor(){
		int result;
		double d1,d2;
		Particle p1,p2;

		p1=(Particle) PCNeighbors.elementAt(0);
		d1=getMinD(p1.x,p1.y);
		result=0;
		for(int i=1;i<neigb;i++){
			p2=(Particle) PCNeighbors.elementAt(i);
			d2=getMinD(p2.x,p2.y);
			if(d2<d1) result=i;
		}

		return result;
	}


    public void findAllParticles(){
		int next;
		//xint limit=0;
		Particle p;
		while(neigb>0){
			next=CloseNeighbor();
			p=(Particle) PCNeighbors.elementAt(next);
			PCNeighbors.removeElementAt(next);
			neigb--;
			if(!InCluster(p)) addParticle(p);
			//limit++;

		}
	}

	public boolean InCluster(Particle p){
		boolean result=false;
		Particle p1;
		for(int i=0;i<num_p;i++){
			p1=(Particle) particles.elementAt(i);
			if(p1==p) result=true;
		}

		return result;
	}



}



class ClusterFile extends Dialog implements ActionListener {

	ImageProcessor ip;
	private ParticleHandler myph;
	private final CheckboxGroup choice = new CheckboxGroup();

	private int num = 0;
	private final Vector listPoints = new Vector(512,512);
	private final Vector listClus = new Vector(128,32);
	private int clust_n=0;
	private double thresh_d = 105;


	public void actionPerformed (final ActionEvent ae){
		this.setVisible(false);

		if (ae.getActionCommand().equals("Open")){
			Frame f = new Frame();
			FileDialog fd = new FileDialog(f,"point list",FileDialog.LOAD);
			fd.setVisible(true);
			String path = fd.getDirectory();
			String filename = fd.getFile();
			if((path==null)||(filename==null)){return;}
			try{
				FileReader fr = new FileReader(path+filename);
				BufferedReader br = new BufferedReader(fr);
				String line;
				String pString;
				String xString;
				String yString;
				int separatorIndex;
				int x;
				int y;
				if ((line = br.readLine()) == null) {
					fr.close();
					return;
				}
				while ((line = br.readLine()) != null) {
					line = line.trim();
					separatorIndex = line.indexOf(' ');
					if (separatorIndex == -1) {
						fr.close();
						IJ.error("Invalid file");
						return;
					}
					line = line.substring(separatorIndex);
					line = line.trim();
					separatorIndex = line.indexOf(' ');
					if (separatorIndex == -1) {
						fr.close();
						IJ.error("Invalid file");
						return;
					}
					xString = line.substring(0, separatorIndex);
					xString = xString.trim();
					line = line.substring(separatorIndex);
					line = line.trim();
					separatorIndex = line.indexOf(' ');
					if (separatorIndex == -1) {
						separatorIndex = line.length();
					}
					yString = line.substring(0, separatorIndex);
					yString = yString.trim();
					x = Integer.parseInt(xString);
					y = Integer.parseInt(yString);
					addPoint(x, y);
				}
				fr.close();
				calculate_neighbors();
				cal_cluster();
				print();
				draw();
			}catch(FileNotFoundException e){
				IJ.error("file not found exception");
			}catch(IOException e){
				IJ.error("IO exception");
			}
		} else if (ae.getActionCommand().equals("Cancel")){
		} else if (ae.getActionCommand().equals("Calculate")){
			Vector list = myph.getPoints();
			int np = myph.getNum();
			for(int i=0;i<np;i++){
				Point p = (Point)list.elementAt(i);
				addPoint(p.x,p.y);
			}
			calculate_neighbors();
			cal_cluster();
			print();
			draw();
		}

	}

	public void setThreshold(double dd){
		thresh_d=dd;
	}



	public void addPoint(int x, int y){
		Particle p = new Particle(x,y,0);
		listPoints.addElement(p);
		num++;
	}

	public void calculate_neighbors(){

		double d,d1;
		Particle p1;
		Particle p2;

		for(int k=0;k<listPoints.size();k++){
			p1=(Particle)listPoints.elementAt(k);

			for(int j=0;j<listPoints.size();j++){
				if(j!=k){
					p2=(Particle)listPoints.elementAt(j);
					d1 = (p2.x-p1.x)*(p2.x-p1.x) + (p2.y-p1.y)*(p2.y-p1.y);
					d = Math.pow((double)d1,0.5);
					if(d<thresh_d) p1.addNeighbor(p2);
				}
			}
		}
	}

	public void sort() {
	         boolean sorted = false;
	         while (!sorted) {
	              sorted = true;
	              for (int i=0; i < listPoints.size()-1; i++) {
	                   Particle first = (Particle) listPoints.elementAt(i);
	                   Particle second = (Particle) listPoints.elementAt(i +1);
	                   if (first.y > second.y) {
	                        Particle temp = first;
	                        listPoints.setElementAt(second, i);
	                        listPoints.setElementAt(temp, i+1);
	                        sorted = false;
	                   }
	              }
	         }
	}

	public void cal_cluster(){
		Particle p1,p2;
		int clust_id=1;
		ParticleCluster pc;
		double dd,d;
		int limit;


		for(int i=0;i<listPoints.size();i++){
			p1=(Particle)listPoints.elementAt(i);

			if(p1.cnum==0){
			pc= new ParticleCluster(clust_id);
			pc.addParticle(p1);
			p1.cnum=clust_id;
			pc.findAllParticles();


            listClus.addElement(pc);
            clust_n++;
			clust_id++;
		}



		}
	}


    public void print(){
		ParticleCluster pc1;
        int [] num1;
        num1 = new int[21];
		int pn;
		int max_size=1;


		IJ.write("**Number of Particles: "+num+"\n");
		IJ.write("**Number of Clusters: "+clust_n+"\n");
		IJ.write("**Threshold of Distance: "+thresh_d+"\n");

		for (int j=0;j<clust_n;j++){
			pc1=(ParticleCluster)listClus.elementAt(j);
			pn=pc1.getNum();
			if(pn>max_size) max_size=pn;
			//IJ.write(pn+"\n");
			for(int i=1;i<=20;i++){
				if(pn==i) num1[i-1]+=pn;
			}
			if(pn>=21) num1[20]+=pn;

		}

		for(int k=1;k<=21;k++){
			IJ.write("**Numb of Particles in Clusters size "+k+" :  "+num1[k-1]+"\n");
		}

        int [] num2;
        num2 = new int[max_size+1];
        for(int j=0;j<max_size+1;j++) num2[j]=0;

		for (int j=0;j<clust_n;j++){
			pc1=(ParticleCluster)listClus.elementAt(j);
			pn=pc1.getNum();
		    num2[pn]+=1;
		}

		int n1=0;
		for(int j=0;j<max_size+1;j++){
			if(num2[j]!=0) n1++;
		}

        IJ.write(max_size+"\n"+1+"\n"+n1+"\n");
		for(int j=0;j<max_size+1;j++){
			if(num2[j]!=0) IJ.write(j+" "+num2[j]+"\n");
		}



	}

	public void draw(){
		IndexColorModel cm = (IndexColorModel)LookUpTable.createGrayscaleColorModel(false);
		byte[] reds = new byte[256];
		byte[] greens = new byte[256];
		byte[] blues = new byte[256];
		cm.getReds(reds);
		cm.getGreens(greens);
		cm.getBlues(blues);
		reds[1] =(byte) 0;
		greens[1] = (byte)0;
		blues[1] = (byte) 255;

		IndexColorModel myLut = new IndexColorModel(8,256,reds,greens,blues);
		ip.setColorModel(myLut);
		ip.setValue(1.0);

		ParticleCluster pc;
		ip.setLineWidth(5);

		for(int i=0;i<clust_n;i++){
			pc=(ParticleCluster)listClus.elementAt(i);
			ip.drawLine(pc.min_x-20,pc.min_y-20,pc.max_x+20,pc.min_y-20);
			ip.drawLine(pc.min_x-20,pc.min_y-20,pc.min_x-20,pc.max_y+20);
			ip.drawLine(pc.min_x-20,pc.max_y+20,pc.max_x+20,pc.max_y+20);
			ip.drawLine(pc.max_x+20,pc.max_y+20,pc.max_x+20,pc.min_y-20);
		}

		new ImagePlus("Cluster Analysis",ip).show();
	}


	ClusterFile (final Frame parentWindow, final ImageProcessor ip, final ParticleHandler ph){
		super(parentWindow,"Cluster Calculation",true);
		this.ip=ip;
		this.myph=ph;
		setLayout(new GridLayout(0, 1));
		final Button openButton = new Button("Open");
		final Button calculateButton = new Button("Calculate");
		final Button cancelButton = new Button("Cancel");
		openButton.addActionListener(this);
		calculateButton.addActionListener(this);
		cancelButton.addActionListener(this);
		final Label separation1 = new Label("");
		final Label separation2 = new Label("");
		add(separation1);
		add(openButton);
		add(calculateButton);
		add(separation2);
		add(cancelButton);
		pack();
	}
}
