package ij.plugin;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.*;
import ij.plugin.filter.Analyzer;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.util.*;

/** This plugin generates gel profile plots that can be analyzed using
the wand tool. It is similar to the "Gel Plotting Macros" in NIH Image. */
public class GelAnalyzer implements PlugIn {

    static final String OPTIONS = "gel.options"; 
    static final String VSCALE = "gel.vscale"; 
    static final String HSCALE = "gel.hscale"; 
    static final int OD=1, PERCENT=2, OUTLINE=4, INVERT=8;
	static int saveID;
	static int nLanes, saveNLanes;
	static Rectangle firstRect;
	static final int MAX_LANES = 100;
	static int[] x = new int[MAX_LANES+1];
	static PlotsCanvas plotsCanvas;
	static ImageProcessor ipLanes;
	static ImagePlus gel;
	static int plotHeight;
	static int options = (int)Prefs.get(OPTIONS, PERCENT+INVERT);
	static boolean uncalibratedOD = (options&OD)!=0;
	static boolean labelWithPercentages = (options&PERCENT)!=0;;
	static boolean outlineLanes;
	static boolean invertPeaks = (options&INVERT)!=0;
	static double verticalScaleFactor = Prefs.get(VSCALE, 1.0);
	static double horizontalScaleFactor = Prefs.get(HSCALE, 1.0);
	static Overlay overlay;
	boolean invertedLut;
	
	ImagePlus imp;
	Font f;
	double odMin=Double.MAX_VALUE, odMax=-Double.MAX_VALUE;
	static boolean isVertical;
	static boolean showLaneDialog = true;
	
	public void run(String arg) {
		if (arg.equals("options")) {
			showDialog();
			return;
		}
		
		imp = WindowManager.getCurrentImage();
		if (imp==null) {
			IJ.noImage();
			return;
		}

		if (arg.equals("reset")) {
			nLanes = 0;
			saveNLanes = 0;
			saveID = 0;
			if (plotsCanvas!=null)
				plotsCanvas.reset();
			ipLanes = null;
			overlay = null;
			if (gel!=null) {
				ImageCanvas ic = gel.getCanvas();
				if (ic!=null) ic.setDisplayList(null);
				gel.draw();
			}
			return;
		}

		if (arg.equals("percent") && plotsCanvas!=null) {
			plotsCanvas.displayPercentages();
			return;
		}

		if (arg.equals("label") && plotsCanvas!=null) {
			if (plotsCanvas.counter==0)
				show("There are no peak area measurements.");
			else
				plotsCanvas.labelPeaks();
			return;
		}

		if (imp.getID()!=saveID) {
			nLanes=0;
			ipLanes = null;
			saveID = 0;
		}

		if (arg.equals("replot")) {
			if (saveNLanes==0) {
				show("The data needed to re-plot the lanes is not available");
				return;
			}
			nLanes = saveNLanes;
			plotLanes(gel, true);
			return;
		}
		
		if (arg.equals("draw")) {
			outlineLanes();
			return;
		}

		Roi roi = imp.getRoi();
		if (roi==null || roi.getType()!=Roi.RECTANGLE) {
			show("Rectangular selection required.");
			return;
		}
		Rectangle rect = roi.getBounds();
		if (nLanes==0) {
			invertedLut = imp.isInvertedLut();
			IJ.register(GelAnalyzer.class);  // keeps this class from being GC'd
		}

		if (arg.equals("first")) {
			selectFirstLane(rect);
			return;
		}

		if (nLanes==0) {
			show("You must first use the \"Select First Lane\" command.");
			return;
		}
		if (arg.equals("next")) {
			selectNextLane(rect);
			return;
		}
		
		if (arg.equals("plot")) {
			if (( isVertical && (rect.x!=x[nLanes]) ) || ( !(isVertical) && (rect.y!=x[nLanes]) )) {
				selectNextLane(rect);
			}
			plotLanes(gel, false);
			return;
		}

	}

	void showDialog() {
		GenericDialog gd = new GenericDialog("Gel Analyzer");
		gd.addNumericField("Vertical scale factor:", verticalScaleFactor, 1);
		gd.addNumericField("Horizontal scale factor:", horizontalScaleFactor, 1);
		gd.addCheckbox("Uncalibrated OD", uncalibratedOD);
		gd.addCheckbox("Label with percentages", labelWithPercentages);
		gd.addCheckbox("Invert peaks", invertPeaks);
		gd.addHelp(IJ.URL+"/docs/menus/analyze.html#gels");
		gd.showDialog();
		if (gd.wasCanceled())
			return;
		verticalScaleFactor = gd.getNextNumber();
		horizontalScaleFactor = gd.getNextNumber();
		uncalibratedOD = gd.getNextBoolean();
		labelWithPercentages = gd.getNextBoolean();
		invertPeaks = gd.getNextBoolean();
		options = 0;
		if (uncalibratedOD) options |= OD;
		if (labelWithPercentages) options |= PERCENT;
		if (invertPeaks) options |= INVERT;
		if (verticalScaleFactor==0.0) verticalScaleFactor=1.0;
		if (horizontalScaleFactor==0.0) horizontalScaleFactor=1.0;
		Prefs.set(OPTIONS, options);
		Prefs.set(VSCALE, verticalScaleFactor);
		Prefs.set(HSCALE, horizontalScaleFactor);
	}


	void selectFirstLane(Rectangle rect) {
		if (rect.width/rect.height>=2 || IJ.altKeyDown()) {
			if (showLaneDialog) {
				String msg = "Are the lanes really horizontal?\n \n"+
					"ImageJ assumes the lanes are\n"+
					"horizontal if the selection is more\n"+
					"than twice as wide as it is tall. Note\n"+
					"that the selection can only be moved\n"+
					"vertically when the lanes are horizontal.";
				GenericDialog gd = new GenericDialog("Gel Analyzer");
				gd.addMessage(msg);
				gd.setOKLabel("Yes");
				gd.showDialog();
				if (gd.wasCanceled()) return;
				showLaneDialog = false;
			}
			isVertical = false;
		} else
			isVertical = true;
			
		/*
		if ( (isVertical && (rect.height/rect.width)<2 ) || (!isVertical && (rect.width/rect.height)<2 ) ) {
			GenericDialog gd = new GenericDialog("Lane Orientation");
			String[] orientations = {"Vertical","Horizontal"};
			int defaultOrientation = isVertical?0:1;
			gd.addChoice("Lane Orientation:", orientations, orientations[defaultOrientation]);
			gd.showDialog();
			if (gd.wasCanceled())
				return;
			String orientation = gd.getNextChoice();
			if(orientation.equals(orientations[0]))
				isVertical=true;
			else
				isVertical=false;
		}
		*/

		IJ.showStatus("Lane 1 selected ("+(isVertical?"vertical":"horizontal")+" lanes)");
		firstRect = rect;
		nLanes = 1;
		saveNLanes = 0;
		if(isVertical)
			x[1] = rect.x;
		else
			x[1] = rect.y;
		gel = imp;
		saveID = imp.getID();
		overlay = null;
		updateRoiList(rect);
	}

	void selectNextLane(Rectangle rect) {
		if (rect.width!=firstRect.width || rect.height!=firstRect.height) {
			show("Selections must all be the same size.");
			return;
		}
		if (nLanesmax)
				max = pp.getMax();
			if (uncalibratedOD)
				profiles[i] = od(profiles[i]);
		}
		if (uncalibratedOD) {
			min = odMin;
			max = odMax;
		}

		if (isVertical)
			plotWidth = firstRect.height;
		else
			plotWidth = firstRect.width;
		if (plotWidth<650)
			plotWidth = 650;
		if (isVertical) {
			if (plotWidth>4*firstRect.height)
				plotWidth = 4*firstRect.height;
		} else {
			if (plotWidth>4*firstRect.width)
				plotWidth = 4*firstRect.width;
		}

		if (verticalScaleFactor==0.0) verticalScaleFactor=1.0;
		if (horizontalScaleFactor==0.0) horizontalScaleFactor=1.0;
		Dimension screen = IJ.getScreenSize();
		if (plotWidth>screen.width-screen.width/6)
			plotWidth = screen.width - screen.width/6;
		plotWidth = (int)(plotWidth*horizontalScaleFactor);
		plotHeight = plotWidth/2;
		if (plotHeight<250) plotHeight = 250;
		// if (plotHeight>500) plotHeight = 500;
		plotHeight = (int)(plotHeight*verticalScaleFactor);
		ImageProcessor ip = new ByteProcessor(plotWidth, topMargin+nLanes*plotHeight+bottomMargin);
		ip.setColor(Color.white);
		ip.fill();
		ip.setColor(Color.black);
		//draw border
		int h= ip.getHeight();
		ip.moveTo(0,0);
		ip.lineTo(plotWidth-1,0);
		ip.lineTo(plotWidth-1, h-1);
		ip.lineTo(0, h-1);
		ip.lineTo(0, 0);
		ip.moveTo(0, h-2);
		ip.lineTo(plotWidth-1, h-2);
		String s = imp.getTitle()+"; ";
		Calibration cal = imp.getCalibration();
		if (cal.calibrated())
			s += cal.getValueUnit();
		else if (uncalibratedOD)
			s += "Uncalibrated OD";
		else
			s += "Uncalibrated";
		ip.moveTo(5,topMargin);
		ip.drawString(s);
		double xScale = (double)plotWidth/profiles[1].length;
		double yScale;
		if ((max-min)==0.0)
			yScale = 1.0;
		else
			yScale = plotHeight/(max-min);
		for (int i=1; i<=nLanes; i++) {
			double[] profile = profiles[i];
			int top = (i-1)*plotHeight + topMargin;
			int base = top+plotHeight;
			ip.moveTo(0, base);
			ip.lineTo((int)(profile.length*xScale), base);
			ip.moveTo(0, base-(int)((profile[0]-min)*yScale));
			for (int j = 1; jodMax) odMax = v;
			profile[i] = v;
		}
		return profile;
	}

	void outlineLanes() {
		if (gel==null || overlay==null) {
			show("Data needed to outline lanes is no longer available.");
			return;
		}
		int lineWidth = (int)(1.0/gel.getCanvas().getMagnification());
		if (lineWidth<1)
			lineWidth = 1;
		Font f = new Font("Helvetica", Font.PLAIN, 12*lineWidth);
		ImageProcessor ip = gel.getProcessor();
		ImageProcessor ipLanes = ip.duplicate();
		if (!(ipLanes instanceof ByteProcessor))
			ipLanes = ipLanes.convertToByte(true);
		ipLanes.setFont(f);
		ipLanes.setLineWidth(lineWidth);
		setCustomLut(ipLanes);
		ImagePlus lanes = new ImagePlus("Lanes of "+gel.getShortTitle(), ipLanes);
		lanes.changes = true;
		lanes.setRoi(gel.getRoi());
		gel.deleteRoi();
		for (int i=0; iscreen.width)
			w = screen.width-loc.x-20;
		if (loc.y+h>screen.height)
			h = screen.height-loc.y-30;
		win.setSize(w, h);
		win.validate();
		repaintWindow();
	}

}


class PlotsCanvas extends ImageCanvas {

	public static final int MAX_PEAKS = 200;

	double[] actual = {428566.00,351368.00,233977.00,99413.00,60057.00,31382.00,
					   14531.00,7843.00,2146.00,752.00,367.00};
	double[] measured = new double[MAX_PEAKS];
	Rectangle[] rect = new Rectangle[MAX_PEAKS];
	int counter;
	ResultsTable rt;

	public PlotsCanvas(ImagePlus imp) {
		super(imp);
	}

	public void mousePressed(MouseEvent e) {
		super.mousePressed(e);
		Roi roi = imp.getRoi();
		if (roi==null)
			return;
		if (roi.getType()==Roi.LINE)
			Roi.setColor(Color.blue);
		else
			Roi.setColor(Color.yellow);
		if (Toolbar.getToolId()!=Toolbar.WAND || IJ.spaceBarDown())
			return;
		if (IJ.shiftKeyDown()) {
			IJ.showMessage("Gel Analyzer", "Unable to measure area because shift key is down.");
			imp.deleteRoi();
			counter = 0;
			return;
		}
		ImageStatistics stats = imp.getStatistics();
		if (counter==0) {
			rt = ResultsTable.getResultsTable();
			rt.reset();
		}
		//IJ.setColumnHeadings(" \tArea");
		double perimeter = roi.getLength();
		String error = "";
		double circularity = 4.0*Math.PI*(stats.pixelCount/(perimeter*perimeter));
		if (circularity<0.025)
			error = " (error?)";
		double area = stats.pixelCount+perimeter/2.0; // add perimeter/2 to account area under border
		Calibration cal = imp.getCalibration();
		area = area*cal.pixelWidth*cal.pixelHeight;
		rect[counter] = roi.getBounds();

		//area += (rect[counter].width/rect[counter].height)*1.5;
		// adjustment for small peaks from NIH Image gel macros

		int places = cal.scaled()?3:0;
		rt.incrementCounter();
		rt.addValue("Area", area);
		rt.show("Results");
		measured[counter] = area;
		if (counter500;
			if (r.height>=(GelAnalyzer.plotHeight-11))
				fits = true;
			if (!fits)
				y = r.y - 2;
			ip.drawString(s, x, y);
		}
		imp.updateAndDraw();
		displayPercentages();
		//Toolbar.getInstance().setTool(Toolbar.RECTANGLE);
		reset();
	}

	void displayPercentages() {
		ResultsTable rt = ResultsTable.getResultsTable();
		rt.reset();
		//IJ.setColumnHeadings(" \tarea\tpercent");
		double total = 0.0;
		for (int i=0; i 

Ads help maintain this website.