package ij.plugin;
import ij.*;
import ij.plugin.filter.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import java.awt.image.*;
import java.math.*;
import java.util.*;
import ij.measure.*;


public class SurfacePlotter implements PlugIn {

	static final int fontSize = 14;
	static int plotWidth = 350;
	static int polygonMultiplier = 100;
	static boolean oneToOne;
	static boolean firstTime = true;
	
	static boolean showWireframe=false;
	static boolean showGrayscale=true;
	static boolean showAxis=true;
	static boolean whiteBackground=false;
	static boolean blackFill=false;
	static boolean smooth = true;

	ImagePlus img;
	int[] x,y;
	boolean invertedLut;
	double angleInDegrees = 35;
	double angle = (angleInDegrees/360.0)*2.0*Math.PI;
	double angle2InDegrees = 15.0;
	double angle2 = (angle2InDegrees/360.0)*2.0*Math.PI;
	double yinc2 = Math.sin(angle2);
	double p1x, p1y;  // left bottom corner
	double p2x, p2y;  // center bottom corner
	double p3x, p3y;  // right  bottom corner
	
	LookUpTable lut;
	
	public void run(String arg) {
		img = WindowManager.getCurrentImage();
		if (img==null)
			{IJ.noImage(); return;}
		if (img.getType()==ImagePlus.COLOR_RGB)
			{IJ.error("Surface Plotter", "Grayscale or pseudo-color image required"); return;}
		invertedLut = img.getProcessor().isInvertedLut();
		if (firstTime) {
			if (invertedLut)
				whiteBackground	= true;
			firstTime = false;			
		}
		if (!showDialog())
			return;

		int stackFlags = IJ.setupDialog(img, 0);
		if(stackFlags == PlugInFilter.DONE)
			return;
		Date start = new Date();
		lut = img.createLut();
		
		if (stackFlags==PlugInFilter.DOES_STACKS && img.getStack().getSize()>1){			
			ImageStack stackSource = img.getStack();
			ImageProcessor ip = stackSource.getProcessor(1);
			ImageProcessor plot = makeSurfacePlot(ip);
			ImageStack stack = new ImageStack(plot.getWidth(), plot.getHeight());
			stack.setColorModel(plot.getColorModel());			
			for (int i=1;i<=stackSource.getSize();i++)
				stack.addSlice(null, plot.duplicate().getPixels());
			stack.setPixels(plot.getPixels(), 1);
			ImagePlus plots = new ImagePlus("Surface Plot", stack);
			plots.show();
			for (int i=2;i<=stackSource.getSize();i++) {
				IJ.showStatus("Drawing slice " + i + "..." + " (" + (100*(i-1)/stackSource.getSize()) + "% done)");
				ip = stackSource.getProcessor(i);
				plot = makeSurfacePlot(ip);
				ImageWindow win = plots.getWindow();
				if (win!=null && win.isClosed()) break;
				stack.setPixels(plot.getPixels(), i);
				plots.setSlice(i);
			}
		} else {
			ImageProcessor plot = makeSurfacePlot(img.getProcessor());
			new ImagePlus("Surface Plot", plot).show();
		}
		
		Date end = new Date();
     		long lstart = start.getTime();
      		long lend = end.getTime();
      		long difference = lend - lstart;
		IJ.register(SurfacePlotter.class);
		IJ.showStatus("Done in "+difference+" msec." );
	}
	
	boolean showDialog() {
		GenericDialog gd = new GenericDialog("Surface Plotter");
		//gd.addNumericField("Plot Width (pixels):", plotWidth, 0);
		//gd.addNumericField("Angle (-90-90 degrees):", angleInDegrees, 0);
		gd.addNumericField("Polygon Multiplier (10-200%):", polygonMultiplier, 0);
		gd.addCheckbox("Draw_Wireframe", showWireframe);
		gd.addCheckbox("Shade", showGrayscale);
		gd.addCheckbox("Draw_Axis", showAxis);
		gd.addCheckbox("Source Background is Lighter", whiteBackground);
		gd.addCheckbox("Fill Plot Background with Black", blackFill);
		gd.addCheckbox("One Polygon Per Line", oneToOne);
		gd.addCheckbox("Smooth", smooth);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		//plotWidth = (int) gd.getNextNumber();
		//angleInDegrees = gd.getNextNumber();
		polygonMultiplier = (int)gd.getNextNumber();
		showWireframe = gd.getNextBoolean();
		showGrayscale = gd.getNextBoolean();
		showAxis = gd.getNextBoolean();
		whiteBackground = gd.getNextBoolean();
		blackFill = gd.getNextBoolean();
		oneToOne = gd.getNextBoolean();
		smooth = gd.getNextBoolean();
		if (showWireframe && !showGrayscale)
			blackFill = false;
		if (polygonMultiplier>400) polygonMultiplier = 400;
		if (polygonMultiplier<10) polygonMultiplier = 10;
		return true;
	}
	
	public ImageProcessor makeSurfacePlot(ImageProcessor ip) {
		ip = ip.duplicate();
		Rectangle roi = img.getProcessor().getRoi();
		ip.setRoi(roi);
		if (!(ip instanceof ByteProcessor)) {
			ip.setMinAndMax(img.getProcessor().getMin(), img.getProcessor().getMax());
			ip = ip.convertToByte(true);
			ip.setRoi(roi);
		}
		double angle = (angleInDegrees/360.0)*2.0*Math.PI;
		int polygons = (int)(plotWidth*(polygonMultiplier/100.0)/4);
		if (oneToOne)
			polygons = roi.height;
		double xinc = 0.8*plotWidth*Math.sin(angle)/polygons;
		double yinc = 0.8*plotWidth*Math.cos(angle)/polygons;
		IJ.showProgress(0.01);
		ip.setInterpolate(!oneToOne);
		ip = ip.resize(plotWidth, polygons);
		int width = ip.getWidth();
		int height = ip.getHeight();
		double min = ip.getMin();
		double max = ip.getMax();
				
		if(invertedLut) ip.invert();
		if(whiteBackground) ip.invert();
		if (smooth) ip.smooth();

		x = new int[width+2];
		y = new int[width+2];
		double xstart = 10.0;
		if (xinc<0.0)
			xstart += Math.abs(xinc)*polygons;
		ByteProcessor ipProfile =new ByteProcessor(width, (int)(256+width*yinc2));
		ipProfile.setValue(255);
		ipProfile.fill();
		double ystart =  yinc2*width;
		int ybase = (int)(ystart+0.5);
		int windowWidth =(int)(plotWidth+polygons*Math.abs(xinc) + 20.0);
		int windowHeight = (int)(ipProfile.getHeight()+polygons*yinc + 10.0);
		
		if(showAxis){
			xstart += 50+20;
			ystart += 10;
			windowWidth += 60+20;
			windowHeight += 20;
			p1x = xstart;
			p1y = ystart+255;
			p2x = xstart+xinc*height;;
			p2y = p1y+yinc*height;
			p3x =  p2x+width-1;
			p3y = p2y- yinc2*width;
		}
		
		if(showGrayscale) {
			int v;
			int[] column = new int[255];
			for(int row=0; row<255; row++) {
				if(whiteBackground)
					v = row;
				else
					v = 255-row;
				column[row] = v;
			}
			int base = ipProfile.getHeight()-255;		
			for(int col=0; colipW)
			ipW = (int) Math.abs(r*Math.cos(-aBaseR+aR));
		if((int) Math.abs(r*Math.sin(-aBaseR+aR))>ipH)
			ipH = (int) Math.abs(r*Math.sin(-aBaseR+aR));
		
		ipW *= 2;
		ipH *= 2;
		
		int tW = w;
		if(ipW>w)
			tW = ipW;
		ImageProcessor ipText = new ByteProcessor(tW, ipH);
		ipText.setFont(new Font("SansSerif", Font.PLAIN, fontSize));
		ipText.setColor(Color.white);
		ipText.fill();
		ipText.setColor(Color.black);
		ipText.setAntialiasedText(true);
		ipText.drawString(s, tW/2-w/2, ipH/2+h/2);
		ipText.setInterpolate(true);
		ipText.rotate(-a);
		ipText.setRoi(tW/2-ipW/2, 0, ipW, ipH);
		ipText = ipText.crop();
		
		//new ImagePlus("test", ipText).show();
		//ip.copyBits(ipText, x, y, Blitter.COPY_TRANSPARENT);

		return ipText;
	}
	
	void clearAboveProfile(ImageProcessor ipProfile, double[] profile, int width, double yinc2) {
		byte[] pixels = (byte[])ipProfile.getPixels();
		double ydelta = 0.0;
		int height = ipProfile.getHeight();
		for(int x=0; x10) {
			plot.setRoi(0, trim, width, height-trim);
			plot = plot.crop();
		}
		return plot;		
	}


	void fixLut(ImageProcessor ip) {
		if(!lut.isGrayscale() && lut.getMapSize() == 256){
			
			for(int y=0;y 

Ads help maintain this website.