package ij.gui;
import java.awt.*;
import java.util.*;
import java.io.*;
import ij.*;
import ij.process.*;
import ij.util.*;
import ij.plugin.Colors;
import ij.plugin.filter.Analyzer;
import ij.macro.Interpreter;
import ij.measure.Calibration;
import ij.measure.Measurements;
import ij.measure.ResultsTable;

/** This class creates an image that line graphs, scatter plots and plots of vector fields
 *	(arrows) can be drawn on and displayed.
 *
 *	Note that the clone() operation is a shallow clone: objects like arrays, the PlotProperties,
 *	PlotObjects, the ImagePlus etc. of the clone remain the same as those of the original.
 *
 * @author Wayne Rasband
 * @author Philippe CARL, CNRS, philippe.carl (AT) unistra.fr (log axes, arrows, ArrayList data)
 * @author Norbert Vischer (overlay range arrows, 'R'eset range, filled plots, dynamic plots, boxes and whiskers)
 * @author Michael Schmid (axis grid/ticks, resizing/panning/changing range, high-resolution, serialization)
 */
public class Plot implements Cloneable {

	/** Text justification. */
	public static final int LEFT=ImageProcessor.LEFT_JUSTIFY, CENTER=ImageProcessor.CENTER_JUSTIFY, RIGHT=ImageProcessor.RIGHT_JUSTIFY;
	/** Legend positions */
	//NOTE: These have only bits of LEGEND_POSITION_MASK set. The flags of the legend are stored as flags of the legend PlotObject.
	//These are saved in the flags of the PlotObject class; thus bits up to 0x0f are reserved
	public static final int TOP_LEFT=0x90, TOP_RIGHT=0xA0, BOTTOM_LEFT=0xB0, BOTTOM_RIGHT=0xC0, AUTO_POSITION=0x80;
	/** Masks out bits for legend positions; if all these bits are off, the legend is turned off */
	private static final int LEGEND_POSITION_MASK = 0xf0;
	/** Legend has its curves in bottom-to-top sequence (otherwise top to bottom) */
	public static final int LEGEND_BOTTOM_UP = 0x100;
	/** Legend erases background (otherwise transparent) */
	public static final int LEGEND_TRANSPARENT = 0x200;
	/** Display points using a circle (5 pixels in diameter if line thickness<=1, otherwise 7). */
	public static final int CIRCLE = 0;
	/** Display points using an X-shaped mark. */
	public static final int X = 1;
	/** Connect points with solid lines. */
	public static final int LINE = 2;
	/** Display points using a square box-shaped mark. */
	public static final int BOX = 3;
	/** Display points using an tiangular mark. */
	public static final int TRIANGLE = 4;
	/** Display points using an cross-shaped mark. */
	public static final int CROSS = 5;
	/** Display points using a single pixel. */
	public static final int DOT = 6;
	/** Draw black lines between the dots and a circle with the given color at each dot */
	public static final int CONNECTED_CIRCLES = 7;
	/** Display points using an diamond-shaped mark. */
	public static final int DIAMOND = 8;
	/** Draw shape using macro code */
	public static final int CUSTOM = 9;
	/** Fill area between line plot and x-axis at y=0. */
	public static final int FILLED = 10;
	/** Draw a bar for each point. */
	public static final int BAR = 11;
	
	/** Names for the shapes as an array */
	final static String[] SHAPE_NAMES = new String[] {
			"Circle", "X", "Line", "Box", "Triangle", "+", "Dot", "Connected Circles", "Diamond", "Custom", "Filled", "Bar"};
	/** Names in nicely sorting order for menus */
	final static String[] SORTED_SHAPES = new String[] {
			SHAPE_NAMES[LINE], SHAPE_NAMES[CONNECTED_CIRCLES], SHAPE_NAMES[FILLED], SHAPE_NAMES[BAR],
			SHAPE_NAMES[CIRCLE], SHAPE_NAMES[BOX], SHAPE_NAMES[TRIANGLE], SHAPE_NAMES[CROSS],
			SHAPE_NAMES[DIAMOND], SHAPE_NAMES[X], SHAPE_NAMES[DOT]};
	/** flag for numeric labels of x-axis ticks */
	public static final int X_NUMBERS = 0x1;
	/** flag for numeric labels of x-axis ticks */
	public static final int Y_NUMBERS = 0x2;
	/** flag for drawing major ticks on linear (non-logarithmic) x axis */
	public static final int X_TICKS = 0x4;
	/** flag for drawing major ticks on linear (non-logarithmic) y axis */
	public static final int Y_TICKS = 0x8;
	/** flag for drawing vertical grid lines for x axis */
	public static final int X_GRID = 0x10;
	/** flag for drawing horizontal grid lines for y axis */
	public static final int Y_GRID = 0x20;
	/** flag for forcing frame to coincide with the grid/ticks in x direction (results in unused space) */
	public static final int X_FORCE2GRID = 0x40;
	/** flag for forcing frame to coincide with the grid/ticks in y direction (results in unused space) */
	public static final int Y_FORCE2GRID = 0x80;
	/** flag for drawing minor ticks on linear (non-logarithmic) x axis */
	public static final int X_MINOR_TICKS = 0x100;
	/** flag for drawing minor ticks on linear (non-logarithmic) y axis */
	public static final int Y_MINOR_TICKS = 0x200;
	/** flag for logarithmic x-axis */
	public static final int X_LOG_NUMBERS = 0x400;
	/** flag for logarithmic y axis */
	public static final int Y_LOG_NUMBERS = 0x800;
	/** flag for ticks (major and minor, if space) on logarithmic x axis */
	public static final int X_LOG_TICKS = 0x1000;
	/** flag for ticks (major and minor, if space) on logarithmic y axis */
	public static final int Y_LOG_TICKS = 0x2000;
	//leave 0x4000, 0x8000 reserved for broken axes?
	/** The default axisFlags, will be modified by PlotWindow.noGridLines and PlotWindow.noTicks (see getDefaultFlags) */
	public static final int DEFAULT_FLAGS =	 X_NUMBERS + Y_NUMBERS + /*X_TICKS + Y_TICKS +*/
			X_GRID + Y_GRID + X_LOG_TICKS + Y_LOG_TICKS;

	/** Flag for addressing the x axis, for copying from a template: copy/write x axis range. */
	// This must be 0x1 because bit shift operations are used for the other axes
	public static final int X_RANGE = 0x1;
	/** Flag for addressing the y axis, for copying from a template: copy/write y axis range */
	public static final int Y_RANGE = 0x2;
	/** Flags for modifying the range of all axes */
	static final int ALL_AXES_RANGE = X_RANGE | Y_RANGE;
	//0x4, 0x8 reserved for secondary axes
	/** Flag for copying from a template: copy plot size */
	public static final int COPY_SIZE = 0x10;
	/** Flag for copying from a template: copy style & text of axis labels */
	public static final int COPY_LABELS = 0x20;
	/** Flag for copying from a template: copy legend */
	public static final int COPY_LEGEND = 0x40;
	/** Flag for copying from a template: copy axis style */
	public static final int COPY_AXIS_STYLE = 0x80;
	/** Flag for copying from a template: copy contents style */
	public static final int COPY_CONTENTS_STYLE = 0x100;

	/** The default margin width left of the plot frame (enough for 5-digit numbers such as unscaled 16-bit
	 *	@deprecated Not a fixed value any more, use getDrawingFrame() to get the drawing area */
	public static final int LEFT_MARGIN = 65;
	/** The default margin width right of the plot frame
	 *	@deprecated Not a fixed value any more, use getDrawingFrame() to get the drawing area */
	public static final int RIGHT_MARGIN = 18;
	/** The default margin width above the plot frame
	 *	@deprecated Not a fixed value any more, use getDrawingFrame() to get the drawing area */
	public static final int TOP_MARGIN = 15;
	/** The default margin width below the plot frame
	 *	@deprecated Not a fixed value any more, use getDrawingFrame() to get the drawing area */
	public static final int BOTTOM_MARGIN = 40;
	/** minimum width of frame area in plot */
	public static final int MIN_FRAMEWIDTH = 160;
	/** minimum width of frame area in plot */
	public static final int MIN_FRAMEHEIGHT = 90;
	/** key in ImagePlus properties to access the plot behind an ImagePlus */
	public static final String PROPERTY_KEY = "thePlot";

	static final float DEFAULT_FRAME_LINE_WIDTH = 1.0001f; //Frame thickness
	private static final int MIN_X_GRIDSPACING = 45;	//minimum distance between grid lines or ticks along x at plot width 0
	private static final int MIN_Y_GRIDSPACING = 30;	//minimum distance between grid lines or ticks along y at plot height 0
	private final double MIN_LOG_RATIO = 3;				//If max/min ratio is less than this, force linear axis even if log required. should be >2
	private static final int LEGEND_PADDING = 4;		//pixels around legend text etc
	private static final int LEGEND_LINELENGTH = 20;	//length of lines in legend
	private static final int USUALLY_ENLARGE = 1, ALWAYS_ENLARGE = 2; //enlargeRange settings
	private static final double RELATIVE_ARROWHEAD_SIZE = 0.2; //arrow heads have 1/5 of vector length
	private static final int MIN_ARROWHEAD_LENGTH = 3;
	private static final int MAX_ARROWHEAD_LENGTH = 20;
	private static Font	 DEFAULT_FONT = FontUtil.getFont("Arial", Font.PLAIN, PlotWindow.fontSize);

	PlotProperties pp = new PlotProperties();		//size, range, formatting etc, for easy serialization
	Vector allPlotObjects = new Vector();	//all curves, labels etc., also serialized for saving/reading
	private PlotVirtualStack stack;
	private boolean grayscaleStack;
	/** For high-resolution plots, everything will be scaled with this number. Otherwise, must be 1.0.
	 *  (creating margins, saving PlotProperties etc only supports scale=1.0) */
	float scale = 1.0f;
	Rectangle frame = null;							//the clip frame, do not use for image scale
	//The following are the margin sizes actually used. They are modified for font size and also scaled for high-resolution plots
	int leftMargin = LEFT_MARGIN, rightMargin = RIGHT_MARGIN, topMargin = TOP_MARGIN, bottomMargin = BOTTOM_MARGIN;
	int frameWidth;									//width corresponding to plot range; frame.width is larger by 1
	int frameHeight;								//height corresponding to plot range; frame.height is larger by 1
	int preferredPlotWidth = PlotWindow.plotWidth;  //default size of plot frame (not taking 'High-Resolution' scale factor into account)
	int preferredPlotHeight = PlotWindow.plotHeight;


	double xMin = Double.NaN, xMax, yMin, yMax;		//current plot range, logarithm if log axis
	double[] currentMinMax = new double[]{Double.NaN, 0, Double.NaN, 0}; //current plot range, xMin, xMax, yMin, yMax (values, not logarithm if log axis)
	double[] defaultMinMax = new double[]{Double.NaN, 0, Double.NaN, 0}; //default plot range
	double[] savedMinMax = new double[]{Double.NaN, 0, Double.NaN, 0};	//keeps previous range for revert
	int[] enlargeRange;								//whether to enlarge the range slightly to avoid values at the axes (0=off, USUALLY_ENLARGE, ALWAYS_ENLARGE)
	boolean logXAxis, logYAxis;						//whether to really use log axis (never for small relative range)

	int templateFlags = COPY_SIZE | COPY_LABELS | COPY_AXIS_STYLE | COPY_CONTENTS_STYLE | COPY_LEGEND;	//for passing on what should be kept when 'live' plotting (PlotMaker)

	Font defaultFont = DEFAULT_FONT;				//default font for labels, axis, etc.
	Font currentFont = defaultFont;					//font as changed by setFont or setFontSize, must never be null
	private double xScale, yScale;					//pixels per data unit
	private int xBasePxl, yBasePxl;					//pixel coordinates corresponding to 0
	private int maxIntervals = 12;					//maximum number of intervals between ticks or grid lines
	private int tickLength = 7;						//length of major ticks
	private int minorTickLength = 3;				//length of minor ticks
	private Color gridColor = new Color(0xc0c0c0);	//light gray
	private Color frameColor = Color.black;			//never modified
	private ImageProcessor ip;
	private ImagePlus imp;							//if we have an ImagePlus, updateAndDraw on changes
	private String title;
	private boolean invertedLut;					//grayscale plots only, set in Edit>Options>Appearance
	private boolean plotDrawn;
	PlotMaker plotMaker;
	private Color currentColor;						//for next objects added
	private Color currentColor2;					//2nd color for next object added (e.g. line for CONNECTED_CIRCLES)
	float currentLineWidth;
	private int currentJustification = LEFT;
	private boolean ignoreForce2Grid;				// after explicit setting of range (limits), ignore 'FORCE2GRID' flags
	//private boolean snapToMinorGrid;			// snap to grid when zooming to selection
	private double barWidth=0.5;						// 0.1-1.0
	private double barWidthInPixels;

	/** Constructs a new Plot.
	 *	Note that the data xValues, yValues passed with the constructor are plotted last,
	 *	with the settings (color, lineWidth) at the time when 'draw' or 'getProcessor' is called.
	 *	These data are plotted as a LINE.
	 * @param title the window title
	 * @param xLabel	the x-axis label
	 * @param yLabel	the y-axis label
	 * @param xValues	the x-coodinates, or null. If null and yValues is not null, integers starting at 0 will be used for x.
	 * @param yValues	the y-coodinates, or null for providing no data yet.
	 */
	public Plot(String title, String xLabel, String yLabel, float[] xValues, float[] yValues) {
		this(title, xLabel, yLabel, xValues, yValues, getDefaultFlags());
	}

	/** This version of the constructor accepts double arrays. */
	public Plot(String title, String xLabel, String yLabel, double[] xValues, double[] yValues) {
		this(title, xLabel, yLabel, xValues!=null?Tools.toFloat(xValues):null, yValues!=null?Tools.toFloat(yValues):null, getDefaultFlags());
	}

	/* Obsolete, replaced by new Plot(title,xLabel,yLabel); add(shape,x,y);. */
	public Plot(String dummy, String title, String xLabel, String yLabel, float[] xValues, float[] yValues) {
		this(title, xLabel, yLabel, xValues, yValues, getDefaultFlags());
	}

	/** This is a version of the constructor with no intial arrays. */
	public Plot(String title, String xLabel, String yLabel) {
		this(title, xLabel, yLabel, (float[])null, (float[])null, getDefaultFlags());
	}

	/** This is a version of the constructor with no intial arrays. */
	public Plot(String title, String xLabel, String yLabel, int flags) {
		this(title, xLabel, yLabel, (float[])null, (float[])null, flags);
	}

	/** This version of the constructor has a 'flags' argument for
		controlling whether ticks, grid, etc. are present and whether the axes are logarithmic */
	public Plot(String title, String xLabel, String yLabel, float[] xValues, float[] yValues, int flags) {
		this.title = title;
		pp.axisFlags = flags;
		setXYLabels(xLabel, yLabel);
		if (yValues != null && yValues.length>0) {
			addPoints(xValues, yValues, /*yErrorBars=*/null, LINE, /*label=*/null);
			allPlotObjects.get(0).flags = PlotObject.CONSTRUCTOR_DATA;
		}	
		
		String[] xCats = labelsInBraces(xLabel);
		String[] yCats = labelsInBraces(yLabel);
		if (xCats.length > 0){
		    xMin = -0.5;
		    xMax = xCats.length - 0.5;
			draw();
		}
		if (yCats.length > 0){
		    yMin = -0.5;
		    yMax = yCats.length - 0.5;
			draw();
		}
	}

	/** This version of the constructor accepts double arrays and has a 'flags' argument. */
	public Plot(String title, String xLabel, String yLabel, double[] xValues, double[] yValues, int flags) {
		this(title, xLabel, yLabel, xValues!=null?Tools.toFloat(xValues):null, yValues!=null?Tools.toFloat(yValues):null, flags);
	}

	/** Constructs a new plot from an InputStream and closes the stream. If the ImagePlus is
	 *  non-null, its title and ImageProcessor are used, but the image displayed is not modified.
	*/
	public Plot(ImagePlus imp, InputStream is) throws IOException, ClassNotFoundException {
		ObjectInputStream in = new ObjectInputStream(is);
		pp = (PlotProperties)in.readObject();
		allPlotObjects = (Vector)in.readObject();
		in.close();
		defaultMinMax = pp.rangeMinMax;
		currentFont = nonNullFont(pp.frame.getFont(), currentFont); // best guess in case we want to add a legend
		getProcessor();     //prepares scale, calibration etc, but does not plot it yet
		this.title = imp != null ? imp.getTitle() : "Untitled Plot";
		if (imp != null) {
			this.imp = imp;
			ip = imp.getProcessor();
			imp.setIgnoreGlobalCalibration(true);
			adjustCalibration(imp.getCalibration());
			imp.setProperty(PROPERTY_KEY, this);
		}
	}

	/** Writes this plot into an OutputStream containing (1) the serialized PlotProperties and
	 *	(2) the serialized Vector of all 'added' PlotObjects. The stream is NOT closed.
	 *	The plot should have been drawn already.
	 */
	 //	 Conversion to Streams can be also used to clone plots (not a shallow clone), but this is rather slow.
	 //	 Sample code:
	 //	  try {
	 //		final PipedOutputStream pos = new PipedOutputStream();
	 //		final PipedInputStream pis = new PipedInputStream(pos);
	 //		new Thread(new Runnable() {
	 //		  final public void run() {
	 //			try {
	 //			  Plot p = new Plot(null, pis);
	 //			  pis.close();
	 //			  pos.close();
	 //			  p.show();
	 //			} catch(Exception e) {IJ.handleException(e);};
	 //		  }
	 //		}, "threadMakingPlotFromStream").start();
	 //		toStream(pos);
	 //	  } catch(Exception e) {IJ.handleException(e);}
	void toStream(OutputStream os) throws IOException {
		//prepare
		for (PlotObject plotObject : pp.getAllPlotObjects())		//make sure all fonts are set properly
			if (plotObject != null)
				plotObject.setFont(nonNullFont(plotObject.getFont(), currentFont));
		pp.rangeMinMax = currentMinMax;
		//write
		ObjectOutputStream out = new ObjectOutputStream(os);
		out.writeObject(pp);
		out.writeObject(allPlotObjects);
	}

	/** Writes this plot into a byte array containing (1) the serialized PlotProperties and
	 *	(2) the serialized Vector of all 'added' PlotObjects.
	 *	The plot should have been drawn already. Returns null on error (which should never happen). */
	public byte[] toByteArray() {
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			toStream(bos);
			bos.close();
			return bos.toByteArray();
		} catch (Exception e) {
			IJ.handleException(e);
			return null;
		}
	}

	/** Returns the title of the image showing the plot (if any) or title of the plot */
	public String getTitle() {
		return imp == null ? title : imp.getTitle();
	}

	/** Sets the x-axis and y-axis range. Updates the image if existing.
	 *  Accepts NaN values to indicate auto-range.
	 */
	public void setLimits(double xMin, double xMax, double yMin, double yMax) {
		
		String[] xCats = labelsInBraces(this.getLabel('x'));
		String[] yCats = labelsInBraces(this.getLabel('y'));	
		boolean containsNaN = (Double.isNaN(xMin + xMax + yMin + yMax));
		if (containsNaN && allPlotObjects.isEmpty())//can't apply auto-range without data
			return;
		boolean[] auto = new boolean[4];
		double[] range = {xMin, xMax, yMin, yMax};
		if (containsNaN) {
			double[] extrema = getMinAndMax(true, ALL_AXES_RANGE);

			for (int jj = 0; jj < 4; jj++)
				if (Double.isNaN(range[jj])) {
					range[jj] = extrema[jj];
					auto[jj] = true;
				}		
			double left = range[0];
			double right = range[1];
			double bottom = range[2];
			double top = range[3];

			//set semi-auto to full-auto if it would result in reverse axis direction
			if ((auto[0] || auto[1]) && (left >= right)) {
				left = extrema[0];
				right = extrema[1];
				auto[0] = true;
				auto[1] = true;
			}
			if ((auto[2] || auto[3]) && (bottom >= top)) {
				bottom = extrema[2];
				top = extrema[3];
				auto[2] = true;
				auto[3] = true;
			}
			//Add 3% extra space to automatic borders
			double extraXLin = (right - left) * 0.03;
			double extraYLin = (top - bottom) * 0.03;
			double extraXLog = (Math.log(right) - Math.log(left)) * 0.03;
			double extraYLog = (Math.log(top) - Math.log(bottom)) * 0.03;

			boolean isLogX = hasFlag(X_LOG_NUMBERS);
			boolean isLogY = hasFlag(Y_LOG_NUMBERS);

			if (auto[0] && !isLogX)
				range[0] = left - extraXLin;//extra space (linear)
			if (auto[1] && !isLogX)
				range[1] = right + extraXLin;
			if (auto[2] && !isLogY)
				range[2] = bottom - extraYLin;
			if (auto[3] && !isLogY)
				range[3] = top + extraYLin;

			if (auto[0] && isLogX)
				range[0] = Math.exp(Math.log(left) - extraXLog);//extra space (log)
			if (auto[1] && isLogX)
				range[1] = Math.exp(Math.log(right) + extraXLog);
			if (auto[2] && isLogY)
				range[2] = Math.exp(Math.log(bottom) - extraYLog);
			if (auto[3] && isLogY)
				range[3] = Math.exp(Math.log(top) + extraYLog);
		}
		defaultMinMax = range;//change pointer of defaultMinMax
		enlargeRange = null;
		ignoreForce2Grid = true;
		if (plotDrawn)
			setLimitsToDefaults(true);
	}

	/** Returns the current limits as an array xMin, xMax, yMin, yMax.
	 *	Note that future versions might return a longer array (e.g. for y2 axis limits) */
	public double[] getLimits() {
		return new double[] {xMin, xMax, yMin, yMax};
	}

	/** Sets the canvas size in (unscaled) pixels and sets the scale to 1.0.
	 * If the scale remains 1.0, this will be the size of the resulting ImageProcessor.
	 * When not called, the canvas size is adjusted for the plot frame size specified
	 * by setFrameSize or otherwise in Edit>Options>Plots. */
	public void setSize(int width, int height) {
		if (ip != null && width == ip.getWidth() && height == ip.getHeight()) return;
		Dimension minSize = getMinimumSize();
		pp.width = Math.max(width, minSize.width);
		pp.height = Math.max(height, minSize.height);
		scale = 1.0f;
		ip = null;
		if (plotDrawn) updateImage();
	}

	/** The size of the plot including borders with axis labels etc., in pixels */
	public Dimension getSize() {
		if (ip == null)
			getBlankProcessor();
		return new Dimension(ip.getWidth(), ip.getHeight());
	}

	/** Sets the plot frame size in (unscaled) pixels. This size does not include the
	 *	borders with the axis labels. Also sets the scale to 1.0.
	 *	This frame size in pixels divided by the data range defines the image scale.
	 *	This method does not check for the minimum size MIN_FRAMEWIDTH, MIN_FRAMEHEIGHT.
	 *	Note that the black frame will have an outer size that is one pixel larger
	 *	(when plotted with a linewidth of one pixel). */
	public void setFrameSize(int width, int height) {
		if (pp.width <= 0) {                //plot not drawn yet? Just remember as preferred size
			preferredPlotWidth = width;
			preferredPlotHeight = height;
			scale = 1.0f;
		} else {
			makeMarginValues();
			width += leftMargin+rightMargin;
			height += topMargin+bottomMargin;
			setSize(width, height);
		}
	}

	/** The minimum plot size including borders, in pixels (at scale=1) */
	public Dimension getMinimumSize() {
		return new Dimension(MIN_FRAMEWIDTH + leftMargin + rightMargin,
				MIN_FRAMEHEIGHT + topMargin + bottomMargin);
	}

	/** Adjusts the format with another plot as a template, using the current
	 *  (usually default) flags of this plot. Used for keeping the style during
	 *  'Live' plotting (PlotMaker).
	 *  plot may be null; then the call has no effect. */
	public void useTemplate(Plot plot) {
		useTemplate(plot, templateFlags);
	}

	/** Adjusts the format (style) with another plot as a template. Flags determine what to
	 *	copy from the template; these can be COPY_SIZE, COPY_LABELS, COPY_AXIS_STYLE,
	 *  COPY_CONTENTS_STYLE, and COPY_LEGEND.
	 *	plot may be null; then the call has no effect. */
	public void useTemplate(Plot plot, int templateFlags) {
		if (plot == null) return;
		this.defaultFont = plot.defaultFont;
		this.currentFont = plot.currentFont;
		this.currentLineWidth = plot.currentLineWidth;
		this.pp.frame = plot.pp.frame.clone();
		this.currentColor = plot.currentColor;
		if ((templateFlags & COPY_AXIS_STYLE) != 0) {
			this.pp.axisFlags = plot.pp.axisFlags;
			this.pp.frame = plot.pp.frame.clone();
		}
		if ((templateFlags & COPY_LABELS) != 0) {
			this.pp.xLabel.label = plot.pp.xLabel.label;
			this.pp.yLabel.label = plot.pp.yLabel.label;
			this.pp.xLabel.setFont(plot.pp.xLabel.getFont());
			this.pp.yLabel.setFont(plot.pp.yLabel.getFont());
		}
		for (int i=0; i>(i/2)&0x1) != 0) {
				currentMinMax[i] = plot.currentMinMax[i];
				if (!plotDrawn) defaultMinMax[i] = plot.currentMinMax[i];
			}
		if ((templateFlags & COPY_LEGEND) != 0 && plot.pp.legend != null)
			this.pp.legend = plot.pp.legend.clone();
		if ((templateFlags & (COPY_LEGEND | COPY_CONTENTS_STYLE)) != 0) {
			int plotPObjectIndex = 0;
			int plotPObjectsSize = plot.allPlotObjects.size();
			for (PlotObject plotObject : allPlotObjects) {
				if (plotObject.type == PlotObject.XY_DATA && !plotObject.hasFlag(PlotObject.HIDDEN)) {
					while(plotPObjectIndex=plotPObjectsSize) break;
					if ((templateFlags & COPY_LEGEND) != 0)
						plotObject.label = plot.allPlotObjects.get(plotPObjectIndex).label;
					if ((templateFlags & COPY_CONTENTS_STYLE) != 0)
						setPlotObjectStyles(plotObject, getPlotObjectStyles(plot.allPlotObjects.get(plotPObjectIndex)));
				}
			}
		}
		if ((templateFlags & COPY_SIZE) != 0)
			setSize(plot.pp.width, plot.pp.height);
		this.templateFlags = templateFlags;
	}

	/** Sets the scale. Everything, including labels, line thicknesses, etc will be scaled by this factor.
	 *  Also multiplies the plot size by this value. Used for 'Create high-resolution plot'.
	 *	Should be called before creating the plot.
	 *  Note that plots with a scale different from 1.0 must not be shown in a PlotWindow, but only as
	 *  simple image in a normal ImageWindow. */
	public void setScale(float scale) {
		this.scale = scale;
		if (scale > 20f) scale = 20f;
		if (scale < 0.7f) scale = 0.7f;
		pp.width = sc(pp.width);
		pp.height = sc(pp.height);
		plotDrawn = false;
	}

	/** Sets the labels of the x and y axes. Labels may be null.
	 *	Call updateImage() thereafter to make the change visible (if it is shown already). */
	public void setXYLabels(String xLabel, String yLabel) {
		pp.xLabel.label = xLabel!=null ? xLabel : "";
		pp.yLabel.label = yLabel!=null ? yLabel : "";
	}

	/** Sets the maximum number of intervals in a plot.
	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */
	public void setMaxIntervals(int intervals) {
			maxIntervals = intervals;
	}

	/** Sets the length of the major tick in pixels.
	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */
	public void setTickLength(int tickLength) {
			tickLength = tickLength;
	}

	/** Sets the length of the minor tick in pixels. */
	public void setMinorTickLength(int minorTickLength) {
			minorTickLength = minorTickLength;
	}

	/** Sets the flags that control the axes format.
	 *	Does not modify the flags for logarithmic axes on/off and the FORCE2GRID flags.
	 *	Call updateImage() thereafter to make the change visible (if it is shown already). */
	public void setFormatFlags(int flags) {
		int unchangedFlags = X_LOG_NUMBERS | Y_LOG_NUMBERS | X_FORCE2GRID | Y_FORCE2GRID;
		flags = flags & (~unchangedFlags);	  //remove flags that should not be affected
		pp.axisFlags = (pp.axisFlags & unchangedFlags) | flags;
	}

	/** Returns the flags that control the axes */
	public int getFlags() {
		return pp.axisFlags;
	}

	/** Sets the X Axis format to Log or Linear.
	 *	Call updateImage() thereafter to make the change visible (if it is shown already). */
	public void setAxisXLog(boolean axisXLog) {
		pp.axisFlags = axisXLog ? pp.axisFlags | X_LOG_NUMBERS : pp.axisFlags & (~X_LOG_NUMBERS);
	}

	/** Sets the Y Axis format to Log or Linear.
	 *	Call updateImage() thereafter to make the change visible (if it is shown already). */
	public void setAxisYLog(boolean axisYLog) {
		pp.axisFlags = axisYLog ? pp.axisFlags | Y_LOG_NUMBERS : pp.axisFlags & (~Y_LOG_NUMBERS);
	}

	/** Sets whether to show major ticks at the x axis.
	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */

	public void setXTicks(boolean xTicks) {
		pp.axisFlags = xTicks ? pp.axisFlags | X_TICKS : pp.axisFlags & (~X_TICKS);
	}

	/** Sets whether to show major ticks at the y axis.
	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */
	public void setYTicks(boolean yTicks) {
		pp.axisFlags = yTicks ? pp.axisFlags | Y_TICKS : pp.axisFlags & (~Y_TICKS);
	}

	/** Sets whether to show minor ticks on the x axis (if linear). Also sets major ticks if true and no grid is set.
	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */
	public void setXMinorTicks(boolean xMinorTicks) {
		pp.axisFlags = xMinorTicks ? pp.axisFlags | X_MINOR_TICKS : pp.axisFlags & (~X_MINOR_TICKS);
		if (xMinorTicks && !hasFlag(X_GRID))
			pp.axisFlags |= X_TICKS;
	}

	/** Sets whether to show minor ticks on the y axis (if linear). Also sets major ticks if true and no grid is set.
	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */
	public void setYMinorTicks(boolean yMinorTicks) {
		pp.axisFlags = yMinorTicks ? pp.axisFlags | Y_MINOR_TICKS : pp.axisFlags & (~Y_MINOR_TICKS);
		if (yMinorTicks && !hasFlag(Y_GRID))
			pp.axisFlags |= Y_TICKS;
	}

	/** Sets the properties of the axes. Call updateImage() thereafter to make the change visible
	 *	(if the image is shown already). */
	public void setAxes(boolean xLog, boolean yLog, boolean xTicks, boolean yTicks, boolean xMinorTicks, boolean yMinorTicks,
			int tickLenght, int minorTickLenght) {
		setAxisXLog		  (xLog);
		setAxisYLog		  (yLog);
		setXMinorTicks	  (xMinorTicks);
		setYMinorTicks	  (yMinorTicks);
		setXTicks		  (xTicks);
		setYTicks		  (yTicks);
		setTickLength	  (tickLenght);
		setMinorTickLength(minorTickLenght);
	}

	/** Sets log scale in x. Call updateImage() thereafter to make the change visible
	 *	(if the image is shown already). */

	public void setLogScaleX() {
		setAxisXLog(true);
	}

	public void setLogScaleY() {
		setAxisYLog(true);
	}

	/** The default flags, taking PlotWindow.noGridLines, PlotWindow.noTicks into account */
	public static int getDefaultFlags() {
		int defaultFlags = 0;
		if (!PlotWindow.noGridLines) //note that log ticks are also needed because the range may span less than a decade, then no grid is visible
			defaultFlags |= X_GRID | Y_GRID | X_NUMBERS | Y_NUMBERS | X_LOG_TICKS | Y_LOG_TICKS;
		if (!PlotWindow.noTicks)
			defaultFlags |= X_TICKS | Y_TICKS | X_MINOR_TICKS | Y_MINOR_TICKS | X_NUMBERS | Y_NUMBERS | X_LOG_TICKS | Y_LOG_TICKS;
		return defaultFlags;
	}

	/** Adds a curve or set of points to this plot, where 'shape' is
		"line", "filled", "bars, "circles", "boxes", "triangles", "crosses",
		 "dots", "diamonds", "x" or "connected". */
	public void add(String shape, double[] xvalues, double[] yvalues) {
		int iShape = toShape(shape);
		addPoints(Tools.toFloat(xvalues), Tools.toFloat(yvalues), null, iShape, iShape==CUSTOM?shape.substring(5, shape.length()):null);
	}

	/** Adds a curve, set of points or error bars to this plot, where 'shape' is
		"line", "filled", "bars, "circles", "boxes", "triangles", "crosses", "dots",
		"diamonds", "x", "connected", "error bars" or "xerror bars". */
	public void add(String shape, double[] yvalues) {
		int iShape = toShape(shape);
		if (iShape==-1)
			addErrorBars(yvalues);
		else if (iShape==-2)
			addHorizontalErrorBars(yvalues);
		else
			addPoints(null, Tools.toFloat(yvalues), null, iShape, iShape==CUSTOM?shape.substring(5, shape.length()):null);
	}

	/** Adds a set of points to the plot or adds a curve if shape is set to LINE.
	 * @param xValues	the x coordinates, or null. If null, integers starting at 0 will be used for x.
	 * @param yValues	the y coordinates (must not be null)
	 * @param yErrorBars error bars in y, may be null
	 * @param shape		CIRCLE, X, BOX, TRIANGLE, CROSS, DIAMOND, DOT, LINE, CONNECTED_CIRCLES
	 * @param label		Label for this curve or set of points, used for a legend and for listing the plots
	 */
	public void addPoints(float[] xValues, float[] yValues, float[] yErrorBars, int shape, String label) {
		if (xValues==null || xValues.length==0) {
			xValues = new float[yValues.length];
			for (int i=0; i 

Ads help maintain this website.