[med-svn] [imagej] 01/08: New upstream version 1.51q

Carnë Draug carandraug+dev at gmail.com
Tue Sep 26 14:32:09 UTC 2017


This is an automated email from the git hooks/post-receive script.

carandraug-guest pushed a commit to branch master
in repository imagej.

commit 808af31796d0df8ca59fabdbeaabfc898d109a8a
Author: Carnë Draug <carandraug+dev at gmail.com>
Date:   Mon Sep 25 20:20:46 2017 +0100

    New upstream version 1.51q
---
 IJ_Props.txt                           |    7 +-
 build.xml                              |    7 +-
 ij/IJ.java                             |   32 +-
 ij/ImageJ.java                         |    2 +-
 ij/ImagePlus.java                      |   53 +-
 ij/Menus.java                          |    2 +-
 ij/Prefs.java                          |    2 +
 ij/gui/ImageCanvas.java                |    4 +-
 ij/gui/ImageWindow.java                |    2 +
 ij/gui/Plot.java                       | 1206 ++++++++++++++++++++------------
 ij/gui/PlotCanvas.java                 |   10 +-
 ij/gui/PlotDialog.java                 |   38 +-
 ij/gui/PlotWindow.java                 |  237 ++++---
 ij/gui/PointRoi.java                   |   30 +-
 ij/gui/ProfilePlot.java                |    4 +-
 ij/gui/Roi.java                        |   22 +-
 ij/gui/RotatedRectRoi.java             |   18 +-
 ij/gui/ShapeRoi.java                   |    1 -
 ij/gui/StackWindow.java                |    9 +-
 ij/gui/Toolbar.java                    |   74 +-
 ij/gui/Wand.java                       |   16 +-
 ij/io/FileInfo.java                    |    5 +-
 ij/io/FileOpener.java                  |    4 +
 ij/io/FileSaver.java                   |   12 +
 ij/io/RoiDecoder.java                  |    2 +-
 ij/io/TiffDecoder.java                 |   20 +-
 ij/io/TiffEncoder.java                 |   14 +
 ij/macro/Functions.java                |  577 ++++++++-------
 ij/macro/Interpreter.java              |    5 +-
 ij/macro/MacroConstants.java           |    6 +-
 ij/measure/ResultsTable.java           |    2 +-
 ij/plugin/BrowserLauncher.java         |   12 +-
 ij/plugin/CalibrationBar.java          |   22 +-
 ij/plugin/Duplicator.java              |  125 +++-
 ij/plugin/FFT.java                     |   12 +-
 ij/plugin/GelAnalyzer.java             |    7 +-
 ij/plugin/MacroInstaller.java          |    2 +-
 ij/plugin/NewPlugin.java               |    4 +-
 ij/plugin/RGBStackConverter.java       |    2 +
 ij/plugin/ScaleBar.java                |   26 +-
 ij/plugin/Selection.java               |    2 +-
 ij/plugin/StackPlotter.java            |   93 +++
 ij/plugin/Straightener.java            |   14 +-
 ij/plugin/WandToolOptions.java         |   12 +-
 ij/plugin/ZProjector.java              |  141 +++-
 ij/plugin/frame/ContrastAdjuster.java  |    5 -
 ij/plugin/frame/Editor.java            |    9 +-
 ij/plugin/frame/LineWidthAdjuster.java |   10 +-
 ij/plugin/frame/MemoryMonitor.java     |    2 +-
 ij/plugin/frame/PlugInDialog.java      |    4 +-
 ij/plugin/frame/Recorder.java          |    6 +-
 ij/process/AutoThresholder.java        |    1 -
 ij/process/FHT.java                    |    2 +-
 ij/process/FloatProcessor.java         |    7 +-
 ij/process/ImageProcessor.java         |   79 ++-
 ij/util/FontUtil.java                  |   56 ++
 macros/SmoothWandTool.txt              |  104 +++
 release-notes.html                     |   24 +-
 58 files changed, 2129 insertions(+), 1077 deletions(-)

diff --git a/IJ_Props.txt b/IJ_Props.txt
index c6ae54b..2c79dc0 100644
--- a/IJ_Props.txt
+++ b/IJ_Props.txt
@@ -153,9 +153,10 @@ stacks10="Reslice [/]...",ij.plugin.Slicer
 stacks11="Orthogonal Views[H]",ij.plugin.Orthogonal_Views
 stacks12="Z Project...",ij.plugin.ZProjector
 stacks13="3D Project...",ij.plugin.Projector
-stacks14="Plot Z-axis Profile",ij.plugin.ZAxisProfiler
-stacks15="Label...",ij.plugin.filter.StackLabeler
-stacks16="Statistics",ij.plugin.Stack_Statistics
+stacks14="Plot XY Profile",ij.plugin.StackPlotter
+stacks15="Plot Z-axis Profile",ij.plugin.ZAxisProfiler
+stacks16="Label...",ij.plugin.filter.StackLabeler
+stacks17="Statistics",ij.plugin.Stack_Statistics
 
 # Plugins installed in the Image/Stacks/Animation submenu
 animation_01="Start Animation [\\]",ij.plugin.Animator("start")
diff --git a/build.xml b/build.xml
index 50eb7b7..e0ebc78 100644
--- a/build.xml
+++ b/build.xml
@@ -6,10 +6,7 @@
     <!-- First, ensure the build directory exists. -->
     <mkdir dir="build" />
     <!-- Build everything; add debug="on" to debug -->
-    <javac srcdir="." destdir="build" optimize="on" source="1.5" target="1.5" debug="on" includeantruntime="false">
-      <!-- The plugins directory only needs to be 
-             present at runtime, not at build time. -->
-      <exclude name="plugins/**"/>
+    <javac srcdir="./ij" destdir="build" optimize="on" source="1.5" target="1.5" debug="on" includeantruntime="false" encoding="utf-8">
     </javac>
   </target>
   
@@ -50,6 +47,7 @@
     <zip zipfile="../src.zip"
        basedir=".."
        includes="source/**"
+       excludes="source/.gdb_history source/.FBCIndex source/.FBCLockFolder/**"
     />
   </target>
 
@@ -59,6 +57,7 @@
     <mkdir dir="../api" />
     <javadoc 
            sourcepath="."
+           encoding="utf-8"
            packagenames="ij.*"
            destdir="../api"
            author="true"
diff --git a/ij/IJ.java b/ij/IJ.java
index 10397cc..f70cd67 100644
--- a/ij/IJ.java
+++ b/ij/IJ.java
@@ -9,6 +9,7 @@ import ij.util.Tools;
 import ij.plugin.frame.Recorder;
 import ij.plugin.frame.ThresholdAdjuster;
 import ij.macro.Interpreter;
+import ij.macro.MacroRunner;
 import ij.measure.Calibration;
 import ij.measure.ResultsTable;
 import ij.measure.Measurements;
@@ -67,6 +68,7 @@ public class IJ {
 	private static DecimalFormat[] sf;
 	private static DecimalFormatSymbols dfs;
 	private static boolean trustManagerCreated;
+	private static String smoothMacro;
 			
 	static {
 		osname = System.getProperty("os.name");
@@ -1451,34 +1453,48 @@ public class IJ {
 	public static int doWand(int x, int y, double tolerance, String mode) {
 		return doWand(getImage(), x, y, tolerance, mode);
 	}
-
+	
 	/** This version of doWand adds an ImagePlus argument. */
 	public static int doWand(ImagePlus img, int x, int y, double tolerance, String mode) {
 		ImageProcessor ip = img.getProcessor();
 		if ((img.getType()==ImagePlus.GRAY32) && Double.isNaN(ip.getPixelValue(x,y)))
 			return 0;
 		int imode = Wand.LEGACY_MODE;
+		boolean smooth = false;
 		if (mode!=null) {
 			if (mode.startsWith("4"))
 				imode = Wand.FOUR_CONNECTED;
 			else if (mode.startsWith("8"))
 				imode = Wand.EIGHT_CONNECTED;
+			smooth = mode.contains("smooth");
+				
 		}
 		Wand w = new Wand(ip);
 		double t1 = ip.getMinThreshold();
-		if (t1==ImageProcessor.NO_THRESHOLD || (ip.getLutUpdateMode()==ImageProcessor.NO_LUT_UPDATE&& tolerance>0.0))
+		if (t1==ImageProcessor.NO_THRESHOLD || (ip.getLutUpdateMode()==ImageProcessor.NO_LUT_UPDATE&& tolerance>0.0)) {
 			w.autoOutline(x, y, tolerance, imode);
-		else
+			smooth = false;
+		} else
 			w.autoOutline(x, y, t1, ip.getMaxThreshold(), imode);
 		if (w.npoints>0) {
 			Roi previousRoi = img.getRoi();
-			int type = Wand.allPoints()?Roi.FREEROI:Roi.TRACED_ROI;
-			Roi roi = new PolygonRoi(w.xpoints, w.ypoints, w.npoints, type);
+			Roi roi = new PolygonRoi(w.xpoints, w.ypoints, w.npoints, Roi.TRACED_ROI);
 			img.deleteRoi();
-			img.setRoi(roi);
-			// add/subtract this ROI to the previous one if the shift/alt key is down
+			img.setRoi(roi);			
 			if (previousRoi!=null)
-				roi.update(shiftKeyDown(), altKeyDown());
+				roi.update(shiftKeyDown(), altKeyDown());  // add/subtract ROI to previous one if shift/alt key down
+			Roi roi2 = img.getRoi();
+			if (smooth && roi2!=null && roi2.getType()==Roi.TRACED_ROI) {
+				Rectangle bounds = roi2.getBounds();
+				if (bounds.width>1 && bounds.height>1) {
+					if (smoothMacro==null)
+						smoothMacro = BatchProcessor.openMacroFromJar("SmoothWandTool.txt");
+					if (EventQueue.isDispatchThread())
+						new MacroRunner(smoothMacro); // run on separate thread
+					else
+						IJ.runMacro(smoothMacro);
+				}
+			}
 		}
 		return w.npoints;
 	}
diff --git a/ij/ImageJ.java b/ij/ImageJ.java
index 7fcf14f..a30f5ff 100644
--- a/ij/ImageJ.java
+++ b/ij/ImageJ.java
@@ -79,7 +79,7 @@ public class ImageJ extends Frame implements ActionListener,
 	MouseListener, KeyListener, WindowListener, ItemListener, Runnable {
 
 	/** Plugins should call IJ.getVersion() or IJ.getFullVersion() to get the version string. */
-	public static final String VERSION = "1.51p";
+	public static final String VERSION = "1.51q";
 	public static final String BUILD = "";
 	public static Color backgroundColor = new Color(237,237,237);
 	/** SansSerif, 12-point, plain font. */
diff --git a/ij/ImagePlus.java b/ij/ImagePlus.java
index 7fad5bb..4120fbf 100644
--- a/ij/ImagePlus.java
+++ b/ij/ImagePlus.java
@@ -425,6 +425,8 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 			//if (compositeImage) stackSize /= nChannels;
 			if (stackSize>1)
 				win = new StackWindow(this);
+			else if (getProperty(Plot.PROPERTY_KEY) != null)
+				win = new PlotWindow(this, (Plot)(getProperty(Plot.PROPERTY_KEY)));
 			else
 				win = new ImageWindow(this);
 			if (roi!=null) roi.setImage(this);
@@ -550,6 +552,9 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		not work as expected if 'imp' is a CompositeImage
 		and this image is not. */
 	public void setImage(ImagePlus imp) {
+		Properties newProperties = imp.getProperties();
+		if (newProperties!=null)
+			newProperties = (Properties)(newProperties.clone());
 		if (imp.getWindow()!=null)
 			imp = imp.duplicate();
 		ImageStack stack2 = imp.getStack();
@@ -567,7 +572,12 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 			updateAndDraw();
 		}
 		setCalibration(imp.getCalibration());
-		setProperty("Info", imp.getProperty("Info"));
+		properties = newProperties;
+		if (getProperty(Plot.PROPERTY_KEY)!=null && win instanceof PlotWindow) {
+			Plot plot = (Plot)(getProperty(Plot.PROPERTY_KEY));
+			((PlotWindow)win).setPlot(plot);
+			plot.setImagePlus(this);
+		}
 	}
 	
 	/** Replaces the ImageProcessor with the one specified and updates the
@@ -601,9 +611,9 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		if (title!=null) setTitle(title);
 		if (ip==null)
 			return;
+		this.ip = ip;
 		if (this.ip!=null && getWindow()!=null)
 			notifyListeners(UPDATED);
-		this.ip = ip;
 		if (ij!=null)
 			ip.setProgressBar(ij.getProgressBar());
         int stackSize = 1;
@@ -1562,9 +1572,11 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 				overlay2 = ip2.getOverlay();
 				if (overlay2!=null)
 					setOverlay(overlay2);
-				Properties props = ((VirtualStack)stack).getProperties();
-				if (props!=null)
-					setProperty("FHT", props.get("FHT"));
+				if (stack instanceof VirtualStack) {
+					Properties props = ((VirtualStack)stack).getProperties();
+					if (props!=null)
+						setProperty("FHT", props.get("FHT"));
+				}
 				pixels = ip2.getPixels();
 			} else
 				pixels = stack.getPixels(currentSlice);
@@ -1649,7 +1661,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		roi.setImage(this);
 		if (updateDisplay)
 			draw();
-		//roi.notifyListeners(RoiListener.CREATED);
+		roi.notifyListeners(RoiListener.CREATED);
 	}
 	
 	/** Creates a rectangular selection. */
@@ -1730,6 +1742,8 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 				}
 				break;
 		}
+		if (roi!=null)
+			roi.notifyListeners(RoiListener.CREATED);
 	}
 
 	/** Deletes the current region of interest. Makes a copy of the ROI
@@ -2079,11 +2093,11 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 			setCalibration(imp.getCalibration());
 	}
 
-	/** Copies attributes (name, ID, calibration, path) of the specified image to this image. */
+	/** Copies attributes (name, ID, calibration, path, plot) of the specified image to this image. */
 	public void copyAttributes(ImagePlus imp) {
 		if (IJ.debugMode) IJ.log("copyAttributes: "+imp.getID()+"  "+this.getID()+" "+imp+"   "+this);
 		if (imp==null || imp.getWindow()!=null)
-			throw new IllegalArgumentException("Souce image is null or displayed");
+			throw new IllegalArgumentException("Source image is null or displayed");
 		ID = imp.getID();
 		setTitle(imp.getTitle());
 		setCalibration(imp.getCalibration());
@@ -2093,6 +2107,9 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		Object info = imp.getProperty("Info");
 		if (info!=null)
 			setProperty("Info", imp.getProperty("Info"));
+		Object plot = imp.getProperty(Plot.PROPERTY_KEY);
+		if (plot != null)
+			setProperty(Plot.PROPERTY_KEY, plot);
 	}
 
     /** Calls System.currentTimeMillis() to save the current
@@ -2493,7 +2510,6 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 	/** Returns a "flattened" version of this image, in RGB format. */
 	public ImagePlus flatten() {
 		if (IJ.debugMode) IJ.log("flatten");
-		IJ.wait(50); // wait for screen to be refreshed
 		ImagePlus imp2 = createImagePlus();
 		imp2.setTitle(flattenTitle);
 		ImageCanvas ic2 = new ImageCanvas(imp2);
@@ -2511,6 +2527,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		Overlay overlay2 = getOverlay();
 		if (overlay2!=null && imp2.getRoi()!=null)
 			imp2.deleteRoi();
+		setPointScale(imp2.getRoi(), overlay2);
 		ic2.setOverlay(overlay2);
 		ImageCanvas ic = getCanvas();
 		if (ic!=null)
@@ -2617,6 +2634,24 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		ImagePlus imp2 = imp1.flatten();
 		stack.setPixels(imp2.getProcessor().getPixels(), slice);
 	}
+	
+	private void setPointScale(Roi roi2, Overlay overlay2) {
+		ImageCanvas ic = getCanvas();
+		if (ic==null)
+			return;
+		double scale = 1.0/ic.getMagnification();
+		if (scale==1.0)
+			return;
+		if (roi2!=null && (roi2 instanceof PointRoi))
+			roi2.setFlattenScale(scale);
+		if (overlay2!=null) {
+			for (int i=0; i<overlay2.size(); i++) {
+				roi2 = overlay2.get(i);
+				if (roi2!=null && (roi2 instanceof PointRoi))
+					roi2.setFlattenScale(scale);
+			}
+		}
+	}
 
 	/** Assigns a LUT (lookup table) to this image.
 	 * @see ij.io.Opener#openLut
diff --git a/ij/Menus.java b/ij/Menus.java
index 6b9a510..46adde4 100644
--- a/ij/Menus.java
+++ b/ij/Menus.java
@@ -314,7 +314,7 @@ public class Menus {
 		submenu = new Menu("Python");
 		addExample(submenu, "Sphere", "Sphere.py");
 		addExample(submenu, "Animated Gaussian Blur", "Animated_Gaussian_Blur.py");
-		addExample(submenu, "Rotational Animation.py", "Rotational_Animation.py");
+		addExample(submenu, "Rotational Animation", "Rotational_Animation.py");
 		addExample(submenu, "Overlay", "Overlay.py");
 		submenu.addActionListener(listener);
 		menu.add(submenu);
diff --git a/ij/Prefs.java b/ij/Prefs.java
index 87aa158..4315210 100644
--- a/ij/Prefs.java
+++ b/ij/Prefs.java
@@ -171,6 +171,8 @@ public class Prefs {
 	public static boolean jFileChooserSettingChanged;
 	/** Convert tiff units to microns if pixel width is less than 0.0001 cm. */
 	public static boolean convertToMicrons = true;
+	/** Wand tool "Smooth if thresholded" option */
+	public static boolean smoothWand;
 
 
 	static Properties ijPrefs = new Properties();
diff --git a/ij/gui/ImageCanvas.java b/ij/gui/ImageCanvas.java
index 2aafa88..980770f 100644
--- a/ij/gui/ImageCanvas.java
+++ b/ij/gui/ImageCanvas.java
@@ -609,7 +609,7 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 						setCursor(crosshairCursor);
 				} else if (roi!=null && roi.getState()!=roi.CONSTRUCTING && roi.isHandle(sx, sy)>=0) {
 					setCursor(handCursor);
-				} else if ((overlay!=null||showAllOverlay!=null) && overOverlayLabel(sx,sy,ox,oy)) {
+				} else if ((overlay!=null||showAllOverlay!=null) && overOverlayLabel(sx,sy,ox,oy) && (roi==null||roi.getState()!=roi.CONSTRUCTING)) {
 					overOverlayLabel = true;
 					setCursor(handCursor);
 				} else if (Prefs.usePointerCursor || (roi!=null && roi.getState()!=roi.CONSTRUCTING && roi.contains(ox, oy)))
@@ -1162,6 +1162,8 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 				}
 				setRoiModState(e, roi, -1);
 				String mode = WandToolOptions.getMode();
+				if (Prefs.smoothWand)
+					mode = mode + " smooth";
 				int npoints = IJ.doWand(ox, oy, tolerance, mode);
 				if (Recorder.record && npoints>0) {
 					if (Recorder.scriptMode())
diff --git a/ij/gui/ImageWindow.java b/ij/gui/ImageWindow.java
index 596f357..0e698f1 100644
--- a/ij/gui/ImageWindow.java
+++ b/ij/gui/ImageWindow.java
@@ -393,6 +393,8 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
 	public boolean close() {
 		boolean isRunning = running || running2;
 		running = running2 = false;
+		if (imp==null)
+			return true;
 		boolean virtual = imp.getStackSize()>1 && imp.getStack().isVirtual();
 		if (isRunning) IJ.wait(500);
 		if (ij==null || ij.quittingViaMacro() || IJ.getApplet()!=null || Interpreter.isBatchMode() || IJ.macroRunning() || virtual)
diff --git a/ij/gui/Plot.java b/ij/gui/Plot.java
index b605b76..c103e1d 100644
--- a/ij/gui/Plot.java
+++ b/ij/gui/Plot.java
@@ -1,6 +1,7 @@
 package ij.gui;
 import java.awt.*;
 import java.util.*;
+import java.io.*;
 import ij.*;
 import ij.process.*;
 import ij.util.*;
@@ -14,12 +15,13 @@ 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 plot objects,
- *	the ImagePlus etc. of the clone remain the same as those of the original.
+ *	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 Michael Schmid (axis grid/ticks, resizing/panning/changing range, high-resolution)
+ * @author Norbert Vischer (overlay range arrows and 'R'eset range, ...)
+ * @author Michael Schmid (axis grid/ticks, resizing/panning/changing range, high-resolution, serialization)
  */
 public class Plot implements Cloneable {
 
@@ -27,6 +29,7 @@ public class Plot implements Cloneable {
 	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;
@@ -86,35 +89,41 @@ public class Plot implements Cloneable {
 	/** 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 flags, will be modified by PlotWindow.noGridLines and PlotWindow.noTicks (see getDefaultFlags) */
+	/** 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 copying from a template: copy x axis range. */
+	/** 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 copying from a template: copy y axis range */
+	/** 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 text of axis labels */
+	/** Flag for copying from a template: copy plot size */
 	public static final int COPY_SIZE = 0x10;
-	/** Flag for copying from a template: copy text of axis labels */
+	/** 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 = 60;
+	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 = 20;
+	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 = 13;
+	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 = 42;
+	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 */
@@ -122,6 +131,7 @@ public class Plot implements Cloneable {
 	/** 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
@@ -131,29 +141,36 @@ public class Plot implements Cloneable {
 	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);
 
 	static final int ZOOM_AS_PREVIOUS = -20202020;	//when zooming at this coordinate, it rather uses the previous zoom center
+
+	PlotProperties pp = new PlotProperties();		//size, range, formatting etc, for easy serialization
+	Vector<PlotObject> allPlotObjects = new Vector<PlotObject>();	//all curves, labels etc., also serialized for saving/reading
+
+	/** 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
-	float scale = 1.0f;								//everything will be scaled with this number so the plot can be resized
-	//The following the margin sizes actually used. They are modified for font size and also scaled for high-resolution plots
+	//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 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 but never logarithmic
+	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)
 
-	Font defaultFont = new Font("Helvetica", Font.PLAIN, PlotWindow.fontSize); //default font for labels etc.
-	Font currentFont;								//font as changed by setFont or setFontSize
-	Font xLabelFont;								//font for text labels
-	Font yLabelFont;
-	String xLabel;									//axis label
-	String yLabel;
-	int templateFlags = COPY_SIZE | COPY_LABELS | COPY_LEGEND;	//for passing on what should be kept when 'live' plotting (PlotMaker)
+	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 double previousXZoom = Double.NaN;
@@ -162,31 +179,21 @@ public class Plot implements Cloneable {
 	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;
-	private int flags;								//these define axis layout
+	private Color frameColor = Color.black;			//never modified
 	private ImageProcessor ip;
 	private ImagePlus imp;							//if we have an ImagePlus, updateAndDraw on changes
-	private boolean frozen;							//modifications (size, range, contents) don't update the ImageProcessor
 	private String title;
 	private boolean invertedLut;					//grayscale plots only, set in Edit>Options>Appearance
-	private boolean isColor;						//whether a color plot is needed
 	private boolean plotDrawn;
-	private boolean antialiasedText = true;
-	int plotWidth = PlotWindow.plotWidth;
-	int plotHeight = PlotWindow.plotHeight;
 	PlotMaker plotMaker;
-	Vector<PlotObject> allPlotObjects = new Vector<PlotObject>();	//all curves, labels etc.
-	PlotObject legend;								//the legend (if any)
 	private Color currentColor;						//for next objects added
 	private Color currentColor2;					//2nd color for next object added (e.g. line for CONNECTED_CIRCLES)
 	float currentLineWidth;
-	float frameLineWidth;
 	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 Color backgroundColor;
-	
-	/** Construct a new PlotWindow.
+
+	/** 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.
@@ -218,13 +225,13 @@ public class Plot implements Cloneable {
 	/** 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 the appearance of ticks, grid, etc. */
+		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;
-		this.flags = flags;
+		pp.axisFlags = flags;
 		setXYLabels(xLabel, yLabel);
 		if (yValues != null && yValues.length>0) {
 			addPoints(xValues, yValues, /*yErrorBars=*/null, LINE, /*label=*/null);
@@ -237,99 +244,165 @@ public class Plot implements Cloneable {
 		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.
+	 *	@see toStream() */
+	public Plot(ImagePlus imp, InputStream is) throws IOException, ClassNotFoundException {
+		ObjectInputStream in = new ObjectInputStream(is);
+		pp = (PlotProperties)in.readObject();
+		allPlotObjects = (Vector<PlotObject>)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();
 	}
 
-    //n__ begin setLimits
-    /**
-     * 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) {
-        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, 0xff);
-
-            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);
-    }
-    //n__ end setLimits
-   
-    /** Returns the current limits as an array xMin, xMax, yMin, yMax.
+	/** 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) {
+		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);
+	}
+	//n__ end setLimits
+
+	/** 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.
+	/** 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
-	 * in Edit>Options>Plots. */
+	 * by setFrameSize or otherwise in Edit>Options>Plots. */
 	public void setSize(int width, int height) {
-		//IJ.log("setSize "+width+"x"+height+ " old: "+ip);
 		if (ip != null && width == ip.getWidth() && height == ip.getHeight()) return;
 		Dimension minSize = getMinimumSize();
-		plotWidth = Math.max(width, minSize.width) - (leftMargin+rightMargin);
-		plotHeight = Math.max(height, minSize.height) - (topMargin+bottomMargin);
+		pp.width = Math.max(width, minSize.width);
+		pp.height = Math.max(height, minSize.height);
 		scale = 1.0f;
 		ip = null;
 		if (plotDrawn) updateImage();
@@ -343,16 +416,22 @@ public class Plot implements Cloneable {
 	}
 
 	/** Sets the plot frame size in (unscaled) pixels. This size does not include the
-	 *	borders with the axis labels.
+	 *	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) {
-		plotWidth = width;
-		plotHeight = height;
-		ip = null;
-		if (plotDrawn) updateImage();
+		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) */
@@ -362,73 +441,85 @@ public class Plot implements Cloneable {
 	}
 
 	/** Adjusts the format with another plot as a template, using the current
-	 *  (usually default) flags of this plot. Does not modify the properties of
-	 *	individual data sets (marker symbol, color, line thickness...).
-	 *	<code>plot</code> may be null; then the call has no effect. */
+	 *  (usually default) flags of this plot. Used for keeping the style during
+	 *  'Live' plotting (PlotMaker).
+	 *  <code>plot</code> may be null; then the call has no effect. */
 	public void useTemplate(Plot plot) {
 		useTemplate(plot, templateFlags);
 	}
-	/** Adjusts the format with another plot as a template. Flags determine what to
-	 *	copy from the template. Does not modify the properties of
-	 *	individual data sets (marker symbol, color, line thickness...).
+
+	/** 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.
 	 *	<code>plot</code> may be null; then the call has no effect. */
 	public void useTemplate(Plot plot, int templateFlags) {
 		if (plot == null) return;
-		this.flags = plot.flags;
 		this.defaultFont = plot.defaultFont;
 		this.currentFont = plot.currentFont;
-		this.xLabelFont = plot.xLabelFont;
-		this.yLabelFont = plot.yLabelFont;
 		this.currentLineWidth = plot.currentLineWidth;
-		this.frameLineWidth = plot.frameLineWidth;
+		this.pp.frame = plot.pp.frame.clone();
 		this.currentColor = plot.currentColor;
-		this.frameColor = plot.frameColor;
-		if ((templateFlags & COPY_LABELS) != 0) {
-			this.xLabel = plot.xLabel;
-			this.yLabel = plot.yLabel;
+		if ((templateFlags & COPY_AXIS_STYLE) != 0) {
+			this.pp.axisFlags = plot.pp.axisFlags;
+			this.pp.frame = plot.pp.frame.clone();
 		}
-		if ((templateFlags & COPY_LEGEND) != 0) {
+		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<currentMinMax.length; i++)
 			if ((templateFlags>>(i/2)&0x1) != 0) {
 				currentMinMax[i] = plot.currentMinMax[i];
 				if (!plotDrawn) defaultMinMax[i] = plot.currentMinMax[i];
 			}
-		if ((templateFlags & COPY_LEGEND) != 0) {
-			if (plot.legend != null)
-				this.legend = plot.legend.clone();
+		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 plotPObjectNumber = plot.allPlotObjects.size();
+			int plotPObjectsSize = plot.allPlotObjects.size();
 			for (PlotObject plotObject : allPlotObjects) {
-				if (plotObject.type == PlotObject.XY_DATA) {
-					while(plotPObjectIndex<plotPObjectNumber && plot.allPlotObjects.get(plotPObjectIndex).type != PlotObject.XY_DATA)
-						plotPObjectIndex++; //skip everything that has no label
-					if (plotPObjectIndex>=plotPObjectNumber) break;
-					plotObject.label = plot.allPlotObjects.get(plotPObjectIndex).label;
+				if (plotObject.type == PlotObject.XY_DATA && !plotObject.hasFlag(PlotObject.HIDDEN)) {
+					while(plotPObjectIndex<plotPObjectsSize &&
+							(plot.allPlotObjects.get(plotPObjectIndex).type != PlotObject.XY_DATA ||
+							 plot.allPlotObjects.get(plotPObjectIndex).hasFlag(PlotObject.HIDDEN)))
+						plotPObjectIndex++; //skip everything that is invisible or has no label
+					if (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)));
 				}
 			}
 		}
-		setFrameSize(plot.plotWidth, plot.plotHeight);
+		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.
-	 *	Should be called before creating the plot. */
+	 *  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) {
-		this.xLabel = xLabel!=null && xLabel.length()>0 ? xLabel : "";
-		this.yLabel = yLabel!=null && yLabel.length()>0 ? yLabel : "";
+		pp.xLabel.label = xLabel!=null ? xLabel : "";
+		pp.yLabel.label = yLabel!=null ? yLabel : "";
 	}
 
-	/** Sets the maximum number of intervals in a plot.	 
+	/** 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;
@@ -451,53 +542,53 @@ public class Plot implements Cloneable {
 	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
-		this.flags = (this.flags & unchangedFlags) | flags;
+		pp.axisFlags = (pp.axisFlags & unchangedFlags) | flags;
 	}
 
 	/** Returns the flags that control the axes */
 	public int getFlags() {
-		return flags;
+		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) {
-		flags = axisXLog ? flags | X_LOG_NUMBERS : flags & (~X_LOG_NUMBERS);
+		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) {
-		flags = axisYLog ? flags | Y_LOG_NUMBERS : flags & (~Y_LOG_NUMBERS);
+		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) {
-		flags = xTicks ? flags | X_TICKS : flags & (~X_TICKS);
+		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) {
-		flags = yTicks ? flags | Y_TICKS : flags & (~Y_TICKS);
+		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) {
-		flags = xMinorTicks ? flags | X_MINOR_TICKS : flags & (~X_MINOR_TICKS);
+		pp.axisFlags = xMinorTicks ? pp.axisFlags | X_MINOR_TICKS : pp.axisFlags & (~X_MINOR_TICKS);
 		if (xMinorTicks && !hasFlag(X_GRID))
-			flags |= X_TICKS;
+			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) {
-		flags = yMinorTicks ? flags | Y_MINOR_TICKS : flags & (~Y_MINOR_TICKS);
+		pp.axisFlags = yMinorTicks ? pp.axisFlags | Y_MINOR_TICKS : pp.axisFlags & (~Y_MINOR_TICKS);
 		if (yMinorTicks && !hasFlag(Y_GRID))
-			flags |= Y_TICKS;
+			pp.axisFlags |= Y_TICKS;
 	}
 
 	/** Sets the properties of the axes. Call updateImage() thereafter to make the change visible
@@ -551,7 +642,7 @@ public class Plot implements Cloneable {
 		allPlotObjects.add(new PlotObject(xValues, yValues, yErrorBars, shape, currentLineWidth, currentColor, currentColor2, label));
 		if (plotDrawn) updateImage();
 	}
-	
+
 	/** Adds a set of points to the plot or adds a curve if shape is set to LINE.
 	 * @param x			the x coordinates
 	 * @param y			the y coordinates
@@ -565,7 +656,7 @@ public class Plot implements Cloneable {
 	public void addPoints(double[] x, double[] y, int shape) {
 		addPoints(Tools.toFloat(x), Tools.toFloat(y), shape);
 	}
-	
+
 	/** This a version of addPoints that works with JavaScript. */
 	public void addPoints(String dummy, float[] x, float[] y, int shape) {
 		addPoints(x, y, shape);
@@ -588,9 +679,9 @@ public class Plot implements Cloneable {
 		else if (str.contains("triangle"))
 			shape = Plot.TRIANGLE;
 		else if (str.contains("cross") || str.contains("+"))
-			shape = Plot.CROSS;		
+			shape = Plot.CROSS;
 		else if (str.contains("dot"))
-			shape = Plot.DOT;		
+			shape = Plot.DOT;
 		else if (str.contains("xerror"))
 			shape = -2;
 		else if (str.contains("error"))
@@ -601,10 +692,10 @@ public class Plot implements Cloneable {
 	}
 
 	/** Adds a set of points to the plot using double ArrayLists.
-	 * Must be called before the plot is displayed. */	
+	 * Must be called before the plot is displayed. */
 	public void addPoints(ArrayList x, ArrayList y, int shape) {
 		addPoints(getDoubleFromArrayList(x), getDoubleFromArrayList(y), shape);
-	}	
+	}
 
 	/** Adds a set of points to the plot or adds a curve if shape is set to LINE.
 	 * @param x			the x-coodinates
@@ -617,10 +708,10 @@ public class Plot implements Cloneable {
 	}
 
 	/** Adds a set of points to the plot using double ArrayLists.
-	 * Must be called before the plot is displayed. */	
+	 * Must be called before the plot is displayed. */
 	public void addPoints(ArrayList x, ArrayList y, ArrayList z, int shape) {
 		addPoints(getDoubleFromArrayList(x), getDoubleFromArrayList(y), getDoubleFromArrayList(z), shape);
-	}	
+	}
 
 	public double[] getDoubleFromArrayList(ArrayList list) {
 		double[] targ = new double[list.size()];
@@ -628,7 +719,7 @@ public class Plot implements Cloneable {
 			targ[i] = ((Double) list.get(i)).doubleValue();
 		return targ;
 	}
-	
+
 	/** Adds a set of points that will be drawn as ARROWs.
 	 * @param x1		the x-coodinates of the beginning of the arrow
 	 * @param y1		the y-coodinates of the beginning of the arrow
@@ -646,10 +737,10 @@ public class Plot implements Cloneable {
 
 	/** Adds a set of vectors to the plot using double ArrayLists.
 	 *	Does not support logarithmic axes.
-	 *	Must be called before the plot is displayed. */ 
+	 *	Must be called before the plot is displayed. */
 	public void drawVectors(ArrayList x1, ArrayList y1, ArrayList x2, ArrayList y2) {
 		drawVectors(getDoubleFromArrayList(x1), getDoubleFromArrayList(y1), getDoubleFromArrayList(x2), getDoubleFromArrayList(y2));
-	}	
+	}
 
 	/** Adds vertical error bars to the last data passed to the plot (via the constructor or addPoints). */
 	public void addErrorBars(float[] errorBars) {
@@ -658,7 +749,7 @@ public class Plot implements Cloneable {
 			mainObject.yEValues = errorBars;
 		else throw new RuntimeException("Plot can't add y error bars without data");
 	}
-	
+
 	/** Adds vertical error bars to the last data passed to the plot (via the constructor or addPoints). */
 	public void addErrorBars(double[] errorBars) {
 		addErrorBars(Tools.toFloat(errorBars));
@@ -690,13 +781,20 @@ public class Plot implements Cloneable {
 		allPlotObjects.add(new PlotObject(label, x, y, currentJustification, currentFont, currentColor, PlotObject.LABEL));
 	}
 
-	/** Adds an automatically positioned legend, where 'labels' is a 
-		newline-delimited list of curve or point lables. To modify the legend's 
-		style, call 'setFont' and 'setLineWidth' before 'addLegend'. */
+	/** Adds an automatically positioned legend, where 'labels' can be a
+		newline-delimited list of curve or point labels in the sequence these data were added.
+		Hidden data sets are ignored.
+		If 'labels' is null or empty, the labels of the data set previously (if any) are used.
+		To modify the legend's style, call 'setFont' and 'setLineWidth' before 'addLegend'. */
 	public void addLegend(String labels) {
 		addLegend(labels, null);
 	}
-	
+
+	/** Adds a legend at the position given in 'options', where 'labels' is a can be a
+		newline-delimited list of curve or point labels in the sequence these data were added.
+		Hidden data sets are ignored.
+		If 'labels' is null or empty, the labels of the data set previously (if any) are used.
+		To modify the legend's style, call 'setFont' and 'setLineWidth' before 'addLegend'. */
 	public void addLegend(String labels, String options) {
 		int flags = Plot.AUTO_POSITION;
 		if (options!=null) {
@@ -719,14 +817,23 @@ public class Plot implements Cloneable {
 
 	/** Adds a legend. The legend will be always drawn last (on top of everything).
 	 *	To modify the legend's style, call 'setFont' and 'setLineWidth' before 'addLegend'
-	 *	@param labels labels of the points or curves in the sequence of the data added, tab-delimited or linefeed-delimited.
+	 *	@param labels labels of the points or curves in the sequence of the data were added, tab-delimited or linefeed-delimited.
 	 *	The labels of the datasets will be set to these values. If null or not given, the labels set
 	 *	previously (if any) will be used.
+	 *  Hidden data sets are ignored.
 	 *	@param flags  Bitwise or of position (AUTO_POSITION, TOP_LEFT etc.), LEGEND_TRANSPARENT, and LEGEND_BOTTOM_UP if desired.
 	 *	Updates the image (if it is shown already). */
 	public void setLegend(String labels, int flags) {
-		legend = new PlotObject(labels, currentLineWidth == 0 ? 1 : currentLineWidth,
-				currentFont == null ? defaultFont : currentFont, currentColor == null ? Color.black : currentColor, flags);
+		if (labels != null && labels.length()>0) {
+			String[] allLabels = labels.split("[\n\t]");
+			int iPart = 0;
+			for (PlotObject plotObject : allPlotObjects)
+				if (plotObject.type == PlotObject.XY_DATA && !plotObject.hasFlag(PlotObject.HIDDEN))
+					if (iPart < allLabels.length)
+						plotObject.label = allLabels[iPart++];
+		}
+		pp.legend = new PlotObject(currentLineWidth == 0 ? 1 : currentLineWidth,
+				currentFont, currentColor == null ? Color.black : currentColor, flags);
 		if (plotDrawn) updateImage();
 	}
 
@@ -735,17 +842,16 @@ public class Plot implements Cloneable {
 	public void setJustification(int justification) {
 		currentJustification = justification;
 	}
-	
-	/** Changes the drawing color for the next objects that will be added to the plot. 
+
+	/** Changes the drawing color for the next objects that will be added to the plot.
 	 *	For selecting the color of the curve passed with the constructor,
 	 *	use <code>setColor</code> before <code>draw</code>.
 	 *	The frame and labels are always drawn in black. */
 	public void setColor(Color c) {
 		currentColor = c;
 		currentColor2 = null;
-		checkForColor(c);
 	}
-	
+
 	public void setColor(String color) {
 		setColor(Colors.getColor(color, Color.black));
 	}
@@ -760,10 +866,8 @@ public class Plot implements Cloneable {
 	public void setColor(Color c, Color c2) {
 		currentColor = c;
 		currentColor2 = c2;
-		checkForColor(c);
-		checkForColor(c2);
 	}
-	
+
 	/** Sets the drawing color for the next objects that will be added to the plot. */
 	public void setColor(String c1, String c2) {
 		setColor(Colors.getColor(c1,Color.black), Colors.getColor(c2,Color.black));
@@ -771,8 +875,7 @@ public class Plot implements Cloneable {
 
 	/** Set the plot frame background color. */
 	public void setBackgroundColor(Color c) {
-		backgroundColor = c;
-		checkForColor(c);
+		pp.frame.color2 = c;
 	}
 
 	/** Set the plot frame background color. */
@@ -794,7 +897,7 @@ public class Plot implements Cloneable {
 	public void drawLine(double x1, double y1, double x2, double y2) {
 		allPlotObjects.add(new PlotObject(x1, y1, x2, y2, currentLineWidth, 0, currentColor, PlotObject.LINE));
 	}
-	
+
 	/** Draws a line using a normalized 0-1, 0-1 coordinate system,
 	 * with (0,0) at the top left and (1,1) at the lower right corner.
 	 * This is the same coordinate system used by addLabel(x,y,label).
@@ -809,10 +912,17 @@ public class Plot implements Cloneable {
 	}
 
 	/** Sets the font for all following drawLabel etc. operations. The currently set font when
-	 *	displaying the plot determines the font of all labels & numbers
+	 *	displaying the plot determines the font of all labels & numbers.
+	 *  After the plot has been shown, sets the font for the numbers and the legend (if present).
 	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */
 	public void setFont(Font font) {
+		if (font == null) font = defaultFont;
 		currentFont = font;
+		if (plotDrawn) {
+			pp.frame.setFont(font);
+			if (pp.legend != null)
+				pp.legend.setFont(font);
+		}
 	}
 
 	/** Sets the font size and style for all following drawLabel etc. operations. This leaves
@@ -820,15 +930,16 @@ public class Plot implements Cloneable {
 	 *	when displaying the plot determines the font of the numbers at the axes.
 	 *	That font also sets the default label font size, which may be overridden by
 	 *	setAxisLabelFontSize or setXLabelFont, setYLabelFont.
+	 *  After the plot has been shown, sets the font for the numbers and the legend (if present).
 	 *	Styles are defined in the Font class, e.g. Font.PLAIN, Font.BOLD.
 	 *	Set <code>style</code> to -1 to leave the style unchanged.
 	 *	Call updateImage() thereafter to make the change visible (if the image is shown already). */
 	public void setFont(int style, float size) {
 		if (size < 9) size = 9f;
 		if (size > 24) size = 24f;
-		Font previousFont = currentFont == null ? defaultFont : currentFont;
+		Font previousFont = nonNullFont(pp.frame.getFont(), currentFont);
 		if (style < 0) style = previousFont.getStyle();
-		setFont(previousFont.deriveFont(size));
+		setFont(previousFont.deriveFont(style, size));
 	}
 
 	/** Sets the size of the x and y label font size and style. Styles are defined
@@ -838,28 +949,27 @@ public class Plot implements Cloneable {
 	public void setAxisLabelFont(int style, float size) {
 		if (size < 9) size = 9f;
 		if (size > 33) size = 33f;
-		Font usualFont = currentFont == null ? defaultFont : currentFont;
-		if (xLabelFont == null) xLabelFont = usualFont;
-		if (yLabelFont == null) yLabelFont = usualFont;
-		setXLabelFont(xLabelFont.deriveFont(style < 0 ? xLabelFont.getStyle() : style, size));
-		setYLabelFont(xLabelFont.deriveFont(style < 0 ? yLabelFont.getStyle() : style, size));
+		pp.xLabel.setFont(nonNullFont(pp.xLabel.getFont(), currentFont));
+		pp.yLabel.setFont(nonNullFont(pp.yLabel.getFont(), currentFont));
+		setXLabelFont(pp.xLabel.getFont().deriveFont(style < 0 ? pp.xLabel.getFont().getStyle() : style, size));
+		setYLabelFont(pp.xLabel.getFont().deriveFont(style < 0 ? pp.yLabel.getFont().getStyle() : style, size));
 	}
 
 	/** Sets the xLabelFont; must not be mull. If this method is not used, the last setFont
 	 *	of setFontSize call before displaying the plot determines the font, or if neither
 	 *	was called, the font size of the Plot Options is used. */
 	public void setXLabelFont(Font font) {
-		xLabelFont = font;
+		pp.xLabel.setFont(font);
 	}
 
 	/** Sets the yLabelFont; must not be null. */
 	public void setYLabelFont(Font font) {
-		yLabelFont = font;
+		pp.yLabel.setFont(font);
 	}
 
 	/** Determines whether to use antialiased text (default true) */
 	public void setAntialiasedText(boolean antialiasedText) {
-		this.antialiasedText = antialiasedText;
+		pp.antialiasedText = antialiasedText;
 	}
 
 	/** Obsolete; replaced by setFont(). */
@@ -867,6 +977,33 @@ public class Plot implements Cloneable {
 		setFont(font);
 	}
 
+	/** Gets the font for xLabel ('x'), yLabel('y'), numbers ('f' for 'frame') or the legend ('l').
+	 *	Returns null if the given PlotObject does not exist or its font is null */
+	Font getFont(char c) {
+		PlotObject plotObject = pp.getPlotObject(c);
+		if (plotObject != null)
+			return plotObject.getFont();
+		else
+			return null;
+	}
+
+	/** Sets the font for xLabel ('x'), yLabel('y'), numbers ('f' for 'frame') or the legend ('l') */
+	void setFont(char c, Font font) {
+		PlotObject plotObject = pp.getPlotObject(c);
+		if (plotObject != null)
+			plotObject.setFont(font);
+	}
+
+	/** Gets the label String of the xLabel ('x'), yLabel('y') or the legend ('l').
+	 *	Returns null if the given PlotObject does not exist or its label is null */
+	String getLabel(char c) {
+		PlotObject plotObject = pp.getPlotObject(c);
+		if (plotObject != null)
+			return plotObject.label;
+		else
+			return null;
+	}
+
 	/** Get the x coordinates of the data set passed with the constructor (if not null)
 	 *	or otherwise of the data set of the first 'addPoints'. Returns null if neither exists */
 	public float[] getXValues() {
@@ -881,26 +1018,28 @@ public class Plot implements Cloneable {
 		return p==null ? null : p.yValues;
 	}
 
-	/** Get an array with human-readable designations of the PlotObjects (curves, labels, ...)
-	 *  in the sequence they are plotted (i.e., foreground last). **/
+	/** Get an array with human-readable designations of the non-hidden PlotObjects (curves, labels, ...)
+	 *	in the sequence they are plotted (i.e., foreground last). **/
 	public String[] getPlotObjectDesignations() {
-	    int nObjects = allPlotObjects.size();
+		int nObjects = allPlotObjects.size();
 		String[] names = new String[nObjects];
 		if (names.length == 0) return names;
-		String[] labels = null;
-		if (legend != null && legend.label != null)
-			labels = legend.label.split("[\t\n]");
-		int iData = 1, iArrow = 1, iLine = 1, iText = 1;                        //Human readable counters of each object type
+		String[] legendLabels = null;
+		if (pp.legend != null && pp.legend.label != null)
+			legendLabels = pp.legend.label.split("[\t\n]");
+		int iData = 1, iArrow = 1, iLine = 1, iText = 1;                //Human readable counters of each object type
 		int firstObject = allPlotObjects.get(0).hasFlag(PlotObject.CONSTRUCTOR_DATA) ? 1 : 0; //PlotObject passed with constructor is plotted last
 		for (int i=0, p=firstObject; i<nObjects; i++, p++) {
-			if (p >= allPlotObjects.size()) p = 0;                              //might be PlotObject passed with Constructor
+			if (p >= allPlotObjects.size())                             //the PlotObject passed with Constructor comes last
+				p = 0;
 			PlotObject plotObject = allPlotObjects.get(p);
+			if (plotObject.hasFlag(PlotObject.HIDDEN)) continue;
 			int type = plotObject.type;
 			String label = plotObject.label;
 			switch (type) {
 				case PlotObject.XY_DATA:
-					names[i] = "Data Set "+iData+": "+(labels!=null && labels.length>=iData ?
-							labels[iData-1] : "(" + plotObject.yValues.length + " data points)");
+					names[i] = "Data Set "+iData+": "+(plotObject.label != null ?
+							plotObject.label : "(" + plotObject.yValues.length + " data points)");
 					iData++;
 					break;
 				case PlotObject.ARROWS:
@@ -917,8 +1056,8 @@ public class Plot implements Cloneable {
 					iLine++;
 					break;
 				case PlotObject.LABEL: case PlotObject.NORMALIZED_LABEL:
-				    String text = plotObject.label.replaceAll("\n"," ");
-				    if (text.length()>45) text = text.substring(0, 40)+"...";
+					String text = plotObject.label.replaceAll("\n"," ");
+					if (text.length()>45) text = text.substring(0, 40)+"...";
 					names[i] = "Text "+iText+": \""+text+'"';
 					iText++;
 					break;
@@ -928,32 +1067,46 @@ public class Plot implements Cloneable {
 	}
 
 	/** Get the style of the i-th PlotObject (curve, label, ...) in the sequence
-	 *  they are plotted (i.e., foreground last), as String with comma delimiters:
-	 *  Main Color, Secondary Color (or "none"), Line Width [, Symbol shape for XY_DATA] **/
+	 *	they are plotted (i.e., foreground last), as String with comma delimiters:
+	 *	Main Color, Secondary Color (or "none"), Line Width [, Symbol shape for XY_DATA] [,hidden] **/
 	public String getPlotObjectStyles(int i) {
 		if (allPlotObjects.get(0).hasFlag(PlotObject.CONSTRUCTOR_DATA)) i++;
-		if (i == allPlotObjects.size()) i = 0;                      //PlotObject passed with Constructor (#0) is last
+		if (i == allPlotObjects.size()) i = 0;						//PlotObject passed with Constructor (#0) is last
 		PlotObject plotObject = allPlotObjects.get(i);
-	    String styleString = Colors.colorToString(plotObject.color) + "," +
+		return getPlotObjectStyles(plotObject);
+	}
+
+	String getPlotObjectStyles(PlotObject plotObject) {
+		String styleString = Colors.colorToString(plotObject.color) + "," +
 				Colors.colorToString(plotObject.color2) + "," +
 				plotObject.lineWidth;
 		if (plotObject.type == PlotObject.XY_DATA)
 			styleString += ","+SHAPE_NAMES[plotObject.shape];
+		if (plotObject.hasFlag(PlotObject.HIDDEN))
+			styleString += ",hidden";
 		return styleString;
 	}
 
 	/** Set the style of the i-th PlotObject (curve, label, ...) in the sequence
-	 *  they are plotted (i.e., foreground last), from a String with comma delimiters:
-	 *  Main Color, Secondary Color (or "none"), Line Width [, Symbol shape for XY_DATA] **/
+	 *	they are plotted (i.e., foreground last), from a String with comma delimiters:
+	 *	Main Color, Secondary Color (or "none"), Line Width [, Symbol shape for XY_DATA] [,hidden] **/
 	public void setPlotObjectStyles(int i, String styleString) {
 		if (allPlotObjects.get(0).hasFlag(PlotObject.CONSTRUCTOR_DATA)) i++;
-		if (i == allPlotObjects.size()) i = 0;                      //PlotObject passed with Constructor (#0) is last
+		if (i == allPlotObjects.size()) i = 0;						//PlotObject passed with Constructor (#0) is last
 		PlotObject plotObject = allPlotObjects.get(i);
+		setPlotObjectStyles(plotObject, styleString);
+	}
+
+	void setPlotObjectStyles(PlotObject plotObject, String styleString) {
 		String[] items = styleString.split(",");
+		int nItems = items.length;
+		if (items[nItems-1].indexOf("hidden") >= 0) {
+			plotObject.setFlag(PlotObject.HIDDEN);
+			nItems = items.length - 1;
+		} else
+			plotObject.unsetFlag(PlotObject.HIDDEN);
 		plotObject.color = Colors.decode(items[0].trim(), plotObject.color);
 		plotObject.color2 = Colors.decode(items[1].trim(), null);
-		checkForColor(plotObject.color);
-		checkForColor(plotObject.color2);
 		float lineWidth = plotObject.lineWidth;
 		if (items.length >= 3) try {
 			plotObject.lineWidth = Float.parseFloat(items[2].trim());
@@ -975,7 +1128,7 @@ public class Plot implements Cloneable {
 	/** Sets the plot range to encompass all data. Updates the image if existing. */
 	public void setLimitsToFit(boolean updateImg) {
 		saveMinMax();
-		currentMinMax = getMinAndMax(true, 0xff);
+		currentMinMax = getMinAndMax(true, ALL_AXES_RANGE);
 		if (Double.isNaN(defaultMinMax[0]))
 			System.arraycopy(currentMinMax, 0, defaultMinMax, 0, currentMinMax.length);
 		if (plotDrawn && updateImg) updateImage();
@@ -988,7 +1141,7 @@ public class Plot implements Cloneable {
 		System.arraycopy(currentMinMax, 0, swap, 0, currentMinMax.length);
 		System.arraycopy(savedMinMax, 0, currentMinMax, 0, currentMinMax.length);
 		System.arraycopy(swap, 0, savedMinMax, 0, currentMinMax.length);
-		updateImage();		
+		updateImage();
 	}
 
 	/** Draws the plot (if not done before) in an ImageProcessor and returns the ImageProcessor with the plot. */
@@ -1008,18 +1161,22 @@ public class Plot implements Cloneable {
 			if (imp.getProcessor() != ip) imp.setProcessor(ip);
 			return imp;
 		} else {
-		    ImagePlus imp = new ImagePlus(title, ip);
-		    setImagePlus(imp);
-    		return imp;
-    	}
+			ImagePlus imp = new ImagePlus(title, ip);
+			setImagePlus(imp);
+			return imp;
+		}
 	}
 
 	/** Sets the ImagePlus where the plot will be displayed. If the ImagePlus is not
 	 *	known otherwise (e.g. from getImagePlus), this is needed for changes such as
 	 *	zooming in to work correctly. It also sets the calibration of the ImagePlus.
-	 *  The ImagePlus is not displayed or updated.
-	 *  'imp' may be null to disconnect the plot from its ImagePlus */
+	 *	The ImagePlus is not displayed or updated unless its ImageProcessor is
+	 *  no that of the current Plot (then it gets this ImageProcessor).
+	 *  Does nothing if imp is unchanged and has the ImageProcessor of this plot.
+	 *	'imp' may be null to disconnect the plot from its ImagePlus */
 	public void setImagePlus(ImagePlus imp) {
+		if (imp != null && imp == this.imp && imp.getProcessor() == ip)
+			return;
 		if (this.imp != null)
 			this.imp.setProperty(PROPERTY_KEY, null);
 		this.imp = imp;
@@ -1027,6 +1184,8 @@ public class Plot implements Cloneable {
 			imp.setIgnoreGlobalCalibration(true);
 			adjustCalibration(imp.getCalibration());
 			imp.setProperty(PROPERTY_KEY, this);
+			if (ip != null && imp.getProcessor() != ip)
+				imp.setProcessor(ip);
 		}
 	}
 
@@ -1042,6 +1201,7 @@ public class Plot implements Cloneable {
 		cal.yOrigin = yBasePxl+yMin*yScale;
 		cal.pixelHeight = 1.0/Math.abs(yScale);
 		cal.setInvertY(yScale >= 0);
+		cal.setXUnit(" "); // avoid 'pixels' for scaled units
 		if (xMin == xMax)
 			xScale = Double.POSITIVE_INFINITY;
 		if (yMin == yMax)
@@ -1056,7 +1216,7 @@ public class Plot implements Cloneable {
 			imp = getImagePlus();
 			WindowManager.setTempCurrentImage(imp);
 			if (getMainCurveObject() != null) {
-				imp.setProperty("XValues", getXValues()); // Allows values to be retrieved by 
+				imp.setProperty("XValues", getXValues()); // Allows values to be retrieved by
 				imp.setProperty("YValues", getYValues()); // by Plot.getValues() macro function
 			}
 			Interpreter.addBatchModeImage(imp);
@@ -1070,7 +1230,7 @@ public class Plot implements Cloneable {
 			} else
 				setImagePlus(null);
 		}
-		PlotWindow pw = new PlotWindow(this);       //note: this may set imp to null if pw has listValues and autoClose are set
+		PlotWindow pw = new PlotWindow(this);		//note: this may set imp to null if pw has listValues and autoClose are set
 		if (IJ.isMacro() && imp!=null) // wait for plot to be displayed
 			IJ.selectWindow(imp.getID());
 		return pw;
@@ -1078,11 +1238,12 @@ public class Plot implements Cloneable {
 
 	/** Draws the plot specified for the first time. Does nothing if the plot has been drawn already.
 	 *	Call getProcessor to retrieve the ImageProcessor with it.
-	 *	Does no action with respect to the ImagePlus (it any) */
+	 *	Does no action with respect to the ImagePlus (if any) */
 	public void draw() {
 		//IJ.log("draw(); plotDrawn="+plotDrawn);
 		if (plotDrawn) return;
 		getInitialMinAndMax();
+		pp.frame.setFont(nonNullFont(pp.frame.getFont(), currentFont)); //make sure we have a number font for calculating the margins
 		getBlankProcessor();
 		drawContents(ip);
 	}
@@ -1091,32 +1252,37 @@ public class Plot implements Cloneable {
 	 *	and the Plot class does no modifications to the ImageProcessor.
 	 *	Changes are recorded nevertheless and become effective with <code>setFrozen(false)</code>. */
 	public void setFrozen(boolean frozen) {
-		this.frozen = frozen;
-		if (!frozen) {	// unfreeze operations ...
+		pp.isFrozen = frozen;
+		if (!pp.isFrozen) {	// unfreeze operations ...
 			if (imp != null && ip != null) {
 				ImageCanvas ic = imp.getCanvas();
-				if (ic instanceof PlotCanvas)
+				if (ic instanceof PlotCanvas) {
 					((PlotCanvas)ic).resetMagnification();
+					imp.setTitle(imp.getTitle());   //update magnification in title
+				}
+				Undo.setup(Undo.TRANSFORM, imp);
 			}
 			updateImage();
-			ImageWindow win = imp.getWindow();
+			ImageWindow win = imp == null ? null : imp.getWindow();
 			if (win != null) win.updateImage(imp); //show any changes made during the frozen state
 		}
 	}
 
 	public boolean isFrozen() {
-		return frozen;
+		return pp.isFrozen;
 	}
-	
+
 	/** Draws the plot again, ignored if the plot has not been drawn before or the plot is frozen
 	 *	If the ImagePlus exist, updates it and its calibration. */
 	public void updateImage() {
-		if (!plotDrawn || frozen) return;
+		if (!plotDrawn || pp.isFrozen) return;
 		getBlankProcessor();
 		drawContents(ip);
 		if (imp == null) return;
 		adjustCalibration(imp.getCalibration());
 		imp.updateAndDraw();
+		if (ip != imp.getProcessor())
+			imp.setProcessor(ip);
 	}
 
 	/** Returns the rectangle where the data are plotted.
@@ -1126,7 +1292,7 @@ public class Plot implements Cloneable {
 	public Rectangle getDrawingFrame() {
 		if (frame == null)
 			getBlankProcessor(); //setup frame if not done yet
-			return new Rectangle(frame.x, frame.y, frameWidth, frameHeight);
+		return new Rectangle(frame.x, frame.y, frameWidth, frameHeight);
 	}
 
 	/** Creates a new high-resolution plot by scaling it and displays that plot if showIt is true.
@@ -1138,6 +1304,7 @@ public class Plot implements Cloneable {
 		} catch (Exception e) {return null;}
 		hiresPlot.ip = null;
 		hiresPlot.imp = null;
+        hiresPlot.pp = pp.clone();
 		if (!plotDrawn) hiresPlot.getInitialMinAndMax();
 		hiresPlot.setScale(scale);
 		hiresPlot.setAntialiasedText(antialiasedText);
@@ -1268,11 +1435,21 @@ public class Plot implements Cloneable {
 		return scale==1 ? font : font.deriveFont(size*scale);
 	}
 
-	/** Converts the plot to color if the given color requires it */
-	void checkForColor(Color c) {
-		if (c == null) return;
-		if (c.getRed() != c.getGreen() || c.getGreen() != c.getBlue())
-			isColor = true;
+	/** Returns whether the plot requires color (not grayscale) */
+	boolean isColored() {
+		for (PlotObject plotObject : allPlotObjects)
+			if (isColored(plotObject.color) || isColored(plotObject.color2))
+				return true;
+		for (PlotObject plotObject : pp.getAllPlotObjects())
+			if (plotObject != null && (isColored(plotObject.color) || isColored(plotObject.color2)))
+				return true;
+		return false;
+	}
+
+	/** Whether a color is non-grayscale, which requires color (not grayscale) for the plot */
+	boolean isColored(Color c) {
+		if (c == null) return false;
+		return c.getRed() != c.getGreen() || c.getGreen() != c.getBlue();
 	}
 
 	/** Draws the plot contents (all PlotObjects and the frame and legend), without axes etc. */
@@ -1296,10 +1473,10 @@ public class Plot implements Cloneable {
 					color = plotObject.color;
 				else
 					plotObject.color = color;
-				if (plotObject.font != null)
-					font = plotObject.font;
+				if (plotObject.getFont() != null)
+					font = plotObject.getFont();
 				else
-					plotObject.font = font;
+					plotObject.setFont(font);
 				//IJ.log("type="+plotObject.type+" color="+plotObject.color);
 				drawPlotObject(plotObject, ip);
 			}
@@ -1318,11 +1495,13 @@ public class Plot implements Cloneable {
 		}
 
 		// finally draw the frame & legend
-		frameLineWidth = lineWidth;
-		if (frameLineWidth == 0) frameLineWidth = 1;
-		if (frameLineWidth > 3) frameLineWidth = 3;
-		ip.setLineWidth(sc(frameLineWidth));
-		ip.setColor(frameColor);
+		if (!plotDrawn && pp.frame.lineWidth==DEFAULT_FRAME_LINE_WIDTH) {  //when modifying PlotObjects styles later, don't change the frame line width any more
+			pp.frame.lineWidth = lineWidth;
+			if (pp.frame.lineWidth == 0) pp.frame.lineWidth = 1;
+			if (pp.frame.lineWidth > 3) pp.frame.lineWidth = 3;
+		}
+		ip.setLineWidth(sc(pp.frame.lineWidth));
+		ip.setColor(pp.frame.color);
 		int x2 = frame.x + frame.width - 1;
 		int y2 = frame.y + frame.height - 1;
 		ip.moveTo(frame.x, frame.y);	// draw the frame. Can't use ip.drawRect because it is inconsistent for different lineWidths
@@ -1330,31 +1509,29 @@ public class Plot implements Cloneable {
 		ip.lineTo(x2, y2);
 		ip.lineTo(frame.x, y2);
 		ip.lineTo(frame.x, frame.y);
-		if (legend != null && (legend.flags & LEGEND_POSITION_MASK) != 0)
-			drawPlotObject(legend, ip);
+		if (pp.legend != null && (pp.legend.flags & LEGEND_POSITION_MASK) != 0)
+			drawPlotObject(pp.legend, ip);
 
 		plotDrawn = true;
 	}
 
 	/** Creates the processor if not existing, clears the background and prepares
 	 *	it for plotting. Also called by the PlotWindow class to prepare the window. */
-	ImageProcessor getBlankProcessor() { 
-		float marginScale = 0.2f + 0.8f*(currentFont == null ? defaultFont : currentFont).getSize2D()/12f;
-		if (marginScale < 0.7f) marginScale = 0.7f;
-		if (marginScale > 2f) marginScale = 2f;
-		leftMargin	 = sc(LEFT_MARGIN*marginScale);
-		rightMargin	 = sc(RIGHT_MARGIN*marginScale);
-		topMargin	 = sc(TOP_MARGIN*marginScale);
-		bottomMargin = sc(BOTTOM_MARGIN*marginScale);
-		frameWidth	= sc(plotWidth);
-		frameHeight = sc(plotHeight);
-		int width  = frameWidth + leftMargin + rightMargin;
-		int height = frameHeight + topMargin + bottomMargin;
-		if (ip == null || width != ip.getWidth() || height != ip.getHeight() || (isColor && (ip instanceof ByteProcessor))) {
-			if (isColor) {
-				ip = new ColorProcessor(width, height);
+	ImageProcessor getBlankProcessor() {
+		makeMarginValues();
+		//IJ.log("Plot.getBlankPr preferredH="+preferredPlotHeight+" pp.h="+pp.height);
+		if (pp.width <= 0 || pp.height <= 0) {
+			pp.width = sc(preferredPlotWidth) + leftMargin + rightMargin;
+			pp.height = sc(preferredPlotHeight) + topMargin + bottomMargin;
+		}
+		frameWidth = pp.width - (leftMargin + rightMargin);
+		frameHeight = pp.height - (topMargin + bottomMargin);
+		boolean isColored = isColored();	//color, not grayscale required?
+		if (ip == null || pp.width != ip.getWidth() || pp.height != ip.getHeight() || (isColored != (ip instanceof ColorProcessor))) {
+			if (isColored) {
+				ip = new ColorProcessor(pp.width, pp.height);
 			} else {
-				ip = new ByteProcessor(width, height);
+				ip = new ByteProcessor(pp.width, pp.height);
 				invertedLut = Prefs.useInvertingLut && !Interpreter.isBatchMode() && IJ.getInstance()!=null;
 				if (invertedLut) ip.invertLut();
 			}
@@ -1367,10 +1544,10 @@ public class Plot implements Cloneable {
 
 		ip.setFont(scFont(defaultFont));
 		ip.setLineWidth(sc(1));
-		ip.setAntialiasedText(antialiasedText);
+		ip.setAntialiasedText(pp.antialiasedText);
 		frame = new Rectangle(leftMargin, topMargin, frameWidth+1, frameHeight+1);
-		if (backgroundColor!=null) {
-			ip.setColor(backgroundColor);
+		if (pp.frame.color2 != null) {	//background color
+			ip.setColor(pp.frame.color2);
 			ip.setRoi(frame);
 			ip.fill();
 			ip.resetRoi();
@@ -1379,6 +1556,19 @@ public class Plot implements Cloneable {
 		return ip;
 	}
 
+	/** Calculates the margin sizes and sets the class variables accordingly */
+	void makeMarginValues() {
+		Font font = nonNullFont(pp.frame.getFont(), currentFont);
+		float marginScale = 0.1f + 0.9f*font.getSize2D()/12f;
+		if (marginScale < 0.7f) marginScale = 0.7f;
+		if (marginScale > 2f) marginScale = 2f;
+		leftMargin	 = sc(LEFT_MARGIN*marginScale);
+		rightMargin	 = sc(RIGHT_MARGIN*marginScale);
+		topMargin	 = sc(TOP_MARGIN*marginScale);
+		bottomMargin = sc(BOTTOM_MARGIN*marginScale);
+		//IJ.log("marginScale="+marginScale+" left margin="+leftMargin);
+	}
+
 	/** Calculate the actual range, major step interval and set variables for data <-> pixels scaling */
 	double[] makeRangeGetSteps() {
 		double[] steps = new double[2];
@@ -1392,7 +1582,7 @@ public class Plot implements Cloneable {
 			double mid = 0.5*(currentMinMax[i+1]+currentMinMax[i]);
 			double relativeRange = Math.abs(range/mid);
 			if (!logAxis)
-				relativeRange = Math.min(relativeRange, Math.abs(range/(defaultMinMax[i+1]-defaultMinMax[i]))); 
+				relativeRange = Math.min(relativeRange, Math.abs(range/(defaultMinMax[i+1]-defaultMinMax[i])));
 			if (range != 0 && relativeRange<1e-4) {
 				currentMinMax[i+1] = mid + 0.5*range*1e-4/relativeRange;
 				currentMinMax[i] = mid - 0.5*range*1e-4/relativeRange;
@@ -1486,41 +1676,49 @@ public class Plot implements Cloneable {
 	}
 
 	void getInitialMinAndMax() {
-		int axisFlags = 0;
-		if (Double.isNaN(defaultMinMax[0])) axisFlags |= X_RANGE;
-		if (Double.isNaN(defaultMinMax[2])) axisFlags |= Y_RANGE;
-		if (axisFlags != 0)
-			defaultMinMax = getMinAndMax(false, axisFlags);
+		int axisRangeFlags = 0;
+		if (Double.isNaN(defaultMinMax[0])) axisRangeFlags |= X_RANGE;
+		if (Double.isNaN(defaultMinMax[2])) axisRangeFlags |= Y_RANGE;
+		if (axisRangeFlags != 0)
+			defaultMinMax = getMinAndMax(false, axisRangeFlags);
 		setLimitsToDefaults(false);			//use the range values to start with, but don't draw yet
 	}
 
-	/** Gets the minimum and maximum values from the first XY_DATA or VECTOR plotObject or all such plotObjects;
-	 *	axisFlags determine for which axis to calculate the min&max (X_RANGE for x axis, Y_RANGE for y axis);
+	/** Gets the minimum and maximum values from the first XY_DATA or ARROWS plotObject or all such plotObjects;
+	 *	axisRangeFlags determine for which axis to calculate the min&max (X_RANGE for x axis, Y_RANGE for y axis);
 	 *	for the other axes the limit is taken from defaultMinMax
 	 *	Array elements returned are xMin, xMax, yMin, yMax. Also sets enlargeRange to tell which limits should be enlarged
 	 *	beyond the minimum or maximum of the data */
-	double[] getMinAndMax(boolean allObjects, int axisFlags) {
+	double[] getMinAndMax(boolean allObjects, int axisRangeFlags) {
 		double[] allMinMax = new double[]{Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE};
 		for (int i=0; i<allMinMax.length; i++)
-			if (((axisFlags>>i/2) & 1)==0)	  //keep default min & max for this axis
+			if (((axisRangeFlags>>i/2) & 1)==0)	  //keep default min & max for this axis
 				allMinMax[i] = defaultMinMax[i];
 		enlargeRange = new int[allMinMax.length];
 		for (PlotObject plotObject : allPlotObjects) {
 			if (plotObject.type == PlotObject.XY_DATA || plotObject.type == PlotObject.ARROWS) {
-				getMinAndMax(allMinMax, enlargeRange, plotObject, axisFlags);
+				getMinAndMax(allMinMax, enlargeRange, plotObject, axisRangeFlags);
 				if (!allObjects) break;
 			}
 		}
+		if (allMinMax[0]==Double.MAX_VALUE && allMinMax[1]==-Double.MAX_VALUE) { // no x values at all? keep previous
+			allMinMax[0] = defaultMinMax[0];
+			allMinMax[1] = defaultMinMax[1];
+		}
+		if (allMinMax[2]==Double.MAX_VALUE && allMinMax[3]==-Double.MAX_VALUE) { // no y values at all? keep previous
+			allMinMax[2] = defaultMinMax[2];
+			allMinMax[3] = defaultMinMax[3];
+		}
 		return allMinMax;
 	}
 
-	/** Gets the minimum and maximum values from an XY_DATA or VECTOR plotObject;
-	 *	axisFlags determine for which axis (X_RANGE for x axis, Y_RANGE for y axis)
+	/** Gets the minimum and maximum values from an XY_DATA or ARROWS plotObject;
+	 *	axisRangeFlags determine for which axis (X_RANGE for x axis, Y_RANGE for y axis)
 	 *	The minimum modifies allMinAndMax[0] (x), allMinAndMax[2] (y); the maximum modifies [1], [3].
 	 *	Sets the enlargeRange variables */
-	void getMinAndMax(double[] allMinAndMax, int[] enlargeRange, PlotObject plotObject, int axisFlags) {
+	void getMinAndMax(double[] allMinAndMax, int[] enlargeRange, PlotObject plotObject, int axisRangeFlags) {
 		if (plotObject.type == PlotObject.XY_DATA) {
-			if ((axisFlags & X_RANGE) != 0) {
+			if ((axisRangeFlags & X_RANGE) != 0) {
 				int suggestedEnlarge = 0;
 				if (plotObject.shape==DOT || plotObject.yEValues != null) //these can't be seen if merging with the frame
 					suggestedEnlarge = ALWAYS_ENLARGE;
@@ -1528,7 +1726,7 @@ public class Plot implements Cloneable {
 					suggestedEnlarge = USUALLY_ENLARGE;
 				getMinAndMax(allMinAndMax, enlargeRange, suggestedEnlarge, 0, plotObject.xValues, plotObject.xEValues);
 			}
-			if ((axisFlags & Y_RANGE) != 0) {
+			if ((axisRangeFlags & Y_RANGE) != 0) {
 				int suggestedEnlarge = 0;
 				if (plotObject.shape==DOT || plotObject.xEValues != null) //these can't be seen if merging with the frame
 					suggestedEnlarge = ALWAYS_ENLARGE;
@@ -1537,11 +1735,11 @@ public class Plot implements Cloneable {
 				getMinAndMax(allMinAndMax, enlargeRange,  suggestedEnlarge, 2, plotObject.yValues, plotObject.yEValues);
 			}
 		} else if (plotObject.type == PlotObject.ARROWS) {
-			if ((axisFlags & X_RANGE) != 0) {
+			if ((axisRangeFlags & X_RANGE) != 0) {
 				getMinAndMax(allMinAndMax, enlargeRange, ALWAYS_ENLARGE, 0, plotObject.xValues, null);
 				getMinAndMax(allMinAndMax, enlargeRange, ALWAYS_ENLARGE, 0, plotObject.xEValues, null);
 			}
-			if ((axisFlags & Y_RANGE) != 0) {
+			if ((axisRangeFlags & Y_RANGE) != 0) {
 				getMinAndMax(allMinAndMax, enlargeRange, ALWAYS_ENLARGE, 2, plotObject.yValues, null);
 				getMinAndMax(allMinAndMax, enlargeRange, ALWAYS_ENLARGE, 2, plotObject.yEValues, null);
 			}
@@ -1600,6 +1798,16 @@ public class Plot implements Cloneable {
 		System.arraycopy(currentMinMax, 0, savedMinMax, 0, currentMinMax.length);
 	}
 
+	/** Returns the first font of the list that is not null, or defaultFont if both are null */
+	Font nonNullFont(Font font1, Font font2) {
+		if (font1 != null)
+			return font1;
+		else if (font2 != null)
+			return font2;
+		else return
+			defaultFont;
+	}
+
 	/** Zooms to a range given in pixels and updates the image */
 	void zoomToRect(Rectangle r) {
 		saveMinMax();
@@ -1611,48 +1819,44 @@ public class Plot implements Cloneable {
 		updateImage();
 	}
 
-    //n__ begin zoomOnRangeArrow
-    /**
-     * Zooms in or out when the user clicks one of the overlay arrows at the
-     * axes. Index numbers start with 0 at the 'down' arrow of the lower side of
-     * the x axis and end with the up arrow at the upper side of the y axis.
-     */
-    void zoomOnRangeArrow(int arrowIndex) {
-        if (arrowIndex < 8) {//0..7 = arrows, 8 = Reset Range
-            int axisIndex = (arrowIndex / 4) * 2;  //0 for x, 2 for y
-            double min = axisIndex == 0 ? xMin : yMin;
-            double max = axisIndex == 0 ? xMax : yMax;
-            double range = max - min;
-            boolean isMin = (arrowIndex % 4) < 2;
-            boolean shrinkRange = arrowIndex % 4 == 1 || arrowIndex % 4 == 2;
-            double factor = Math.sqrt(2);
-            if (shrinkRange)
-                factor = 1.0 / factor;
-            if (isMin)
-                min = max - range * factor;
-            else
-                max = min + range * factor;
-            boolean logAxis = axisIndex == 0 ? logXAxis : logYAxis;
-            if (logAxis) {
-                min = Math.pow(10, min);
-                max = Math.pow(10, max);
-            }
-            currentMinMax[axisIndex] = min;
-            currentMinMax[axisIndex + 1] = max;
-        }
-
-        if (arrowIndex == 8)
-            setLimitsToDefaults(false);
-        updateImage();
-    }
-//n__ end zoomOnRangeArrow
-    
-    
+	/**
+	 * Zooms in or out when the user clicks one of the overlay arrows at the
+	 * axes. Index numbers start with 0 at the 'down' arrow of the lower side of
+	 * the x axis and end with the up arrow at the upper side of the y axis.
+	 */
+	void zoomOnRangeArrow(int arrowIndex) {
+		if (arrowIndex < 8) {//0..7 = arrows, 8 = Reset Range
+			int axisIndex = (arrowIndex / 4) * 2;  //0 for x, 2 for y
+			double min = axisIndex == 0 ? xMin : yMin;
+			double max = axisIndex == 0 ? xMax : yMax;
+			double range = max - min;
+			boolean isMin = (arrowIndex % 4) < 2;
+			boolean shrinkRange = arrowIndex % 4 == 1 || arrowIndex % 4 == 2;
+			double factor = Math.sqrt(2);
+			if (shrinkRange)
+				factor = 1.0 / factor;
+			if (isMin)
+				min = max - range * factor;
+			else
+				max = min + range * factor;
+			boolean logAxis = axisIndex == 0 ? logXAxis : logYAxis;
+			if (logAxis) {
+				min = Math.pow(10, min);
+				max = Math.pow(10, max);
+			}
+			currentMinMax[axisIndex] = min;
+			currentMinMax[axisIndex + 1] = max;
+		}
+
+		if (arrowIndex == 8)
+			setLimitsToDefaults(false);
+		updateImage();
+	}
+
 	/** Zooms in or out on a point x, y in screen coordinates. If x>0, default in both directions,
 	 *	if the cursor is below the x axis, only in x direction, if the cursor is left of the y axis, only in y direction.
 	 *	If x < 0, zooms on center; if x == ZOOM_AS_PREVIOUS, zooms on the center of the previous zoom
 	 *	operation */
-
 	void zoom(int x, int y, double zoomFactor) {
 		boolean zoomIn = zoomFactor > 1.0;
 		boolean zoomAsPrevious = x==ZOOM_AS_PREVIOUS && (!Double.isNaN(previousXZoom) || !Double.isNaN(previousYZoom));
@@ -1716,7 +1920,7 @@ public class Plot implements Cloneable {
 			currentMinMax[2] += dy/yScale;
 			currentMinMax[3] += dy/yScale;
 		}
-		updateImage();		
+		updateImage();
 	}
 
 	/** Whether to draw simple axes without ticks, grid and numbers only for min, max*/
@@ -1731,16 +1935,15 @@ public class Plot implements Cloneable {
 	/** Draws ticks, grid and axis label for each tick/grid line.
 	 *	The grid or major tick spacing in each direction is given by steps */
 	void drawAxesTicksGridNumbers(double[] steps) {
-		Font scFont = scFont(currentFont==null ? defaultFont : currentFont);
-		Font scFontMedium = scFont.deriveFont(scFont.getSize2D()*10f/12f); //for y-axis numbers if full size does not fit
+		Font scFont = scFont(pp.frame.getFont());
+		Font scFontMedium = scFont.deriveFont(scFont.getSize2D()*10f/12f); //for axis numbers if full size does not fit
 		Font scFontSmall = scFont.deriveFont(scFont.getSize2D()*9f/12f);   //for subscripts
-		int extraWidth = scFont.getSize()/3; //right-justified font has extra space at the right
 		ip.setFont(scFont);
 		FontMetrics fm = ip.getFontMetrics();
 		int fontAscent = fm.getAscent();
 		ip.setJustification(LEFT);
-		int yOfXAxisNumbers = topMargin + frameHeight + fm.getHeight()*5/4 + sc(3);
 		// ---	A l o n g	X	A x i s
+		int yOfXAxisNumbers = topMargin + frameHeight + fm.getHeight()*5/4 + sc(2);
 		if (hasFlag(X_NUMBERS | (logXAxis ? (X_TICKS | X_MINOR_TICKS) : X_LOG_TICKS) + X_GRID)) {
 			Font baseFont = scFont;
 			boolean majorTicks = logXAxis ? hasFlag(X_LOG_TICKS) : hasFlag(X_TICKS);
@@ -1791,7 +1994,7 @@ public class Plot implements Cloneable {
 					}
 				}
 				boolean haveMinorLogNumbers = i2-i1 < 2;		//nunbers on log minor ticks only if < 2 decades
-				if (minorTicks	&& (!logXAxis || step > 1.1)) { //'standard' log minor ticks only for full decades
+				if (minorTicks && (!logXAxis || step > 1.1)) {  //'standard' log minor ticks only for full decades
 					step = niceNumber(step*0.19);				//non-log: 4 or 5 minor ticks per major tick
 					if (logXAxis && step < 1) step = 1;
 					i1 = (int)Math.ceil (Math.min(xMin,xMax)/step-1.e-10);
@@ -1815,16 +2018,19 @@ public class Plot implements Cloneable {
 								ip.drawLine(x, y2, x, y2-sc(minorTickLength));
 								if (m<=minorNumberLimit)
 									drawExpString(Math.pow(10,v), 0, x, yOfXAxisNumbers-fontAscent/2, CENTER, fontAscent, baseFont, scFontSmall);
-							}	  
+							}
 						}
 					}
 				}
 			}
 		}
 		// ---	A l o n g	Y	A x i s
+		ip.setFont(scFont);
 		int maxNumWidth = 0;
+		int xNumberRight = leftMargin-sc(2)-ip.getStringWidth("0")/2;
+		Rectangle rect = ip.getStringBounds("0169");
+		int yNumberOffset = -rect.y-rect.height/2;
 		if (hasFlag(Y_NUMBERS | (logYAxis ? (Y_TICKS | Y_MINOR_TICKS) : Y_LOG_TICKS) + Y_GRID)) {
-			ip.setFont(scFont);
 			ip.setJustification(RIGHT);
 			Font baseFont = scFont;
 			boolean majorTicks = logYAxis ? hasFlag(Y_LOG_TICKS) : hasFlag(Y_TICKS);
@@ -1840,14 +2046,19 @@ public class Plot implements Cloneable {
 					String s = IJ.d2s(yMin,getDigits(yMin, 0.001*yMin, 5));
 					maxNumWidth = ip.getStringWidth(s);
 					int y = yBasePxl;
-					ip.drawString(s, leftMargin-1, y+fontAscent/2+sc(1));
+					ip.drawString(s, xNumberRight, y+fontAscent/2+sc(1));
 				}
 			} else {
-				int w1 = ip.getStringWidth(IJ.d2s(currentMinMax[2], logYAxis ? -1 : digits));
-				int w2 = ip.getStringWidth(IJ.d2s(currentMinMax[3], logYAxis ? -1 : digits));
+				int digitsForWidth = logYAxis ? -1 : digits;
+				if (digitsForWidth < 0) {
+                    digitsForWidth--; //"1.0*10^5" etc. needs more space than 1.0E5
+					xNumberRight += sc(1)+ip.getStringWidth("0")/4;
+				}
+				int w1 = ip.getStringWidth(IJ.d2s(currentMinMax[2], digitsForWidth));
+				int w2 = ip.getStringWidth(IJ.d2s(currentMinMax[3], digitsForWidth));
 				int wMax = Math.max(w1,w2);
 				if (hasFlag(Y_NUMBERS)) {
-					if (wMax > leftMargin-sc(2)-extraWidth - (yLabel.length()>0 ? fm.getHeight() : 0)) {
+					if (wMax > xNumberRight - sc(4) - (pp.yLabel.label.length()>0 ? fm.getHeight() : 0)) {
 						baseFont = scFontMedium;   //small font if there is not enough space for the numbers
 						ip.setFont(baseFont);
 					}
@@ -1869,45 +2080,45 @@ public class Plot implements Cloneable {
 						int w = 0;
 						if (logYAxis || digits<0) {
 							w = drawExpString(logYAxis ? Math.pow(10,v) : v, logYAxis ? -1 : -digits,
-									leftMargin, y, RIGHT, fontAscent, baseFont, scFontSmall);
+									xNumberRight, y, RIGHT, fontAscent, baseFont, scFontSmall);
 						} else {
 							String s = IJ.d2s(v,digits);
 							w = ip.getStringWidth(s);
-							ip.drawString(s, leftMargin-1, y+fontAscent*2/3+1);
+							ip.drawString(s, xNumberRight, y+yNumberOffset);
 						}
 						if (w > maxNumWidth) maxNumWidth = w;
 					}
 				}
-			}
-			boolean haveMinorLogNumbers = i2-i1 < 2;		//nunbers on log minor ticks only if < 2 decades
-			if (yMin!=yMax && minorTicks	&& (!logYAxis || step > 1.1)) { //'standard' log minor ticks only for full decades
-				step = niceNumber(step*0.19);				//non-log: 4 or 5 minor ticks per major tick
-				if (logYAxis && step < 1) step = 1;
-				i1 = (int)Math.ceil (Math.min(yMin,yMax)/step-1.e-10);
-				i2 = (int)Math.floor(Math.max(yMin,yMax)/step+1.e-10);
-				for (int i=i1; i<=i2; i++) {
-					double v = i*step;
-					int y = topMargin + frameHeight - (int)Math.round((v - yMin)*yScale);
-					ip.drawLine(x1, y, x1+sc(minorTickLength), y);
-					ip.drawLine(x2, y, x2-sc(minorTickLength), y);
+				boolean haveMinorLogNumbers = i2-i1 < 2;        //nunbers on log minor ticks only if < 2 decades
+				if (minorTicks && (!logYAxis || step > 1.1)) {  //'standard' log minor ticks only for full decades
+					step = niceNumber(step*0.19);               //non-log: 4 or 5 minor ticks per major tick
+					if (logYAxis && step < 1) step = 1;
+					i1 = (int)Math.ceil (Math.min(yMin,yMax)/step-1.e-10);
+					i2 = (int)Math.floor(Math.max(yMin,yMax)/step+1.e-10);
+					for (int i=i1; i<=i2; i++) {
+						double v = i*step;
+						int y = topMargin + frameHeight - (int)Math.round((v - yMin)*yScale);
+						ip.drawLine(x1, y, x1+sc(minorTickLength), y);
+						ip.drawLine(x2, y, x2-sc(minorTickLength), y);
+					}
 				}
-			}
-			if (logYAxis && majorTicks && Math.abs(yScale)>sc(MIN_X_GRIDSPACING)) {		 //minor ticks for log within the decade
-				int minorNumberLimit = haveMinorLogNumbers ? (int)(0.4*Math.abs(yScale)/fm.getHeight()) : 0;	//more numbers on minor ticks when zoomed in
-				i1 = (int)Math.floor(Math.min(yMin,yMax)-1.e-10);
-				i2 = (int)Math.ceil(Math.max(yMin,yMax)+1.e-10);
-				for (int i=i1; i<=i2; i++) {
-					for (int m=2; m<10; m++) {
-						double v = i+Math.log10(m);
-						if (v > Math.min(yMin,yMax) && v < Math.max(yMin,yMax)) {
-							int y = topMargin + frameHeight - (int)Math.round((v - yMin)*yScale);
-							ip.drawLine(x1, y, x1+sc(minorTickLength), y);
-							ip.drawLine(x2, y, x2-sc(minorTickLength), y);
-							if (m<=minorNumberLimit) {
-								int w = drawExpString(Math.pow(10,v), 0, leftMargin-sc(1), y, RIGHT, fontAscent, baseFont, scFontSmall);
-								if (w > maxNumWidth) maxNumWidth = w;
+				if (logYAxis && majorTicks && Math.abs(yScale)>sc(MIN_X_GRIDSPACING)) {		 //minor ticks for log within the decade
+					int minorNumberLimit = haveMinorLogNumbers ? (int)(0.4*Math.abs(yScale)/fm.getHeight()) : 0;	//more numbers on minor ticks when zoomed in
+					i1 = (int)Math.floor(Math.min(yMin,yMax)-1.e-10);
+					i2 = (int)Math.ceil(Math.max(yMin,yMax)+1.e-10);
+					for (int i=i1; i<=i2; i++) {
+						for (int m=2; m<10; m++) {
+							double v = i+Math.log10(m);
+							if (v > Math.min(yMin,yMax) && v < Math.max(yMin,yMax)) {
+								int y = topMargin + frameHeight - (int)Math.round((v - yMin)*yScale);
+								ip.drawLine(x1, y, x1+sc(minorTickLength), y);
+								ip.drawLine(x2, y, x2-sc(minorTickLength), y);
+								if (m<=minorNumberLimit) {
+									int w = drawExpString(Math.pow(10,v), 0, xNumberRight, y, RIGHT, fontAscent, baseFont, scFontSmall);
+									if (w > maxNumWidth) maxNumWidth = w;
+								}
 							}
-						}	  
+						}
 					}
 				}
 			}
@@ -1915,8 +2126,8 @@ public class Plot implements Cloneable {
 		// --- Write min&max of range if simple style without any axis format flags
 		ip.setFont(scFont);
 		ip.setJustification(LEFT);
-		String xLabelToDraw = xLabel;
-		String yLabelToDraw = yLabel;
+		String xLabelToDraw = pp.xLabel.label;
+		String yLabelToDraw = pp.yLabel.label;
 		if (simpleYAxis()) { // y-axis min&max
 			int digits = getDigits(yMin, yMax, 0.001*(yMax-yMin), 6);
 			String s = IJ.d2s(yMax, digits);
@@ -1944,12 +2155,12 @@ public class Plot implements Cloneable {
 		} else
 			y += sc(1);
 		// --- Write x and y axis text labels
-		ip.setFont(xLabelFont == null ? scFont : scFont(xLabelFont));
-		ip.drawString(xLabelToDraw, leftMargin+(frame.width-ip.getStringWidth(xLabel))/2, y+ip.getFontMetrics().getHeight());
-		if (yLabel.length() > 0) {
-			int xOfYLabel = leftMargin-maxNumWidth-extraWidth-sc(4);
-			if (xOfYLabel < 0) xOfYLabel = 0;
-			drawYLabel(yLabelToDraw, xOfYLabel, topMargin, frame.height, yLabelFont == null ? scFont : scFont(yLabelFont));
+		ip.setFont(pp.xLabel.getFont() == null ? scFont : scFont(pp.xLabel.getFont()));
+		ip.drawString(xLabelToDraw, leftMargin+(frame.width-ip.getStringWidth(xLabelToDraw))/2, y+ip.getFontMetrics().getHeight());
+		if (yLabelToDraw.length() > 0) {
+			int xRightOfYLabel = xNumberRight - maxNumWidth - sc(2);
+			Font yLabelFont = pp.yLabel.getFont() == null ? scFont : scFont(pp.yLabel.getFont());
+			drawYLabel(yLabelToDraw, xRightOfYLabel, topMargin, frame.height, yLabelFont);
 		}
 	}
 
@@ -2019,9 +2230,11 @@ public class Plot implements Cloneable {
 	}
 
 	static int getDigits2(double n, double resolution, int maxDigits) {
+		if (Double.isNaN(n) || Double.isInfinite(n))
+			return 0; //no scientific notation
 		int log10ofN = (int)Math.floor(Math.log10(Math.abs(n))+1e-7);
 		int digits = resolution != 0 ?
-				-(int)Math.floor(Math.log10(Math.abs(resolution))+1e-7) : 
+				-(int)Math.floor(Math.log10(Math.abs(resolution))+1e-7) :
 				Math.max(0, -log10ofN+maxDigits-2);
 		int sciDigits = -Math.max((log10ofN+digits),1);
 		//IJ.log("n="+(float)n+"digitsRaw="+digits+" log10ofN="+log10ofN+" sciDigits="+sciDigits);
@@ -2045,6 +2258,7 @@ public class Plot implements Cloneable {
 		int type = plotObject.type;
 		switch (type) {
 			case PlotObject.XY_DATA:
+				if (plotObject.hasFlag(PlotObject.HIDDEN)) break;
 				ip.setClipRect(frame);
 				if (plotObject.yEValues != null)
 					drawVerticalErrorBars(plotObject.xValues, plotObject.yValues, plotObject.yEValues);
@@ -2056,7 +2270,7 @@ public class Plot implements Cloneable {
 					ip.setColor(plotObject.color2 == null ? Color.black : plotObject.color2);
 				if (drawLine)
 					//draw line
-					drawFloatPolyline(ip, plotObject.xValues, plotObject.yValues, 
+					drawFloatPolyline(ip, plotObject.xValues, plotObject.yValues,
 							Math.min(plotObject.xValues.length, plotObject.yValues.length));
 				if (drawMarker) {
 					int markSize = plotObject.getMarkerSize();
@@ -2127,8 +2341,8 @@ public class Plot implements Cloneable {
 			case PlotObject.LABEL:
 			case PlotObject.NORMALIZED_LABEL:
 				ip.setJustification(plotObject.justification);
-				if (plotObject.font != null)
-					ip.setFont(scFont(plotObject.font));
+				if (plotObject.getFont() != null)
+					ip.setFont(scFont(plotObject.getFont()));
 				int xt = type==PlotObject.LABEL ? scaleX(plotObject.x) : leftMargin + (int)(plotObject.x*frameWidth);
 				int yt = type==PlotObject.LABEL ? scaleY(plotObject.y) : topMargin + (int)(plotObject.y*frameHeight);
 				ip.drawString(plotObject.label, xt, yt);
@@ -2215,7 +2429,8 @@ public class Plot implements Cloneable {
 	}
 
 	/** Adds an arrow from position 1 to 2 given in pixels; 'size' is the length of the arrowhead
-	 *	WARNING: Use as a public method is not supported any more because it is incompatible with rescaling */
+	 *	@deprecated Use as a public method is not supported any more because it is incompatible with rescaling */
+	@Deprecated
 	public void drawArrow(int x1, int y1, int x2, int y2, double size) {
 		double dx = x2 - x1;
 		double dy = y2 - y1;
@@ -2231,7 +2446,7 @@ public class Plot implements Cloneable {
 		int y5 = (int) Math.round(y3 + dx * r);
 		ip.moveTo(x1, y1); ip.lineTo(x2, y2);
 		ip.moveTo(x4, y4); ip.lineTo(x2, y2); ip.lineTo(x5, y5);
-	}	
+	}
 
 	private void drawVerticalErrorBars(float[] x, float[] y, float[] e) {
 		int nPoints = Math.min(Math.min(x.length, y.length), e.length);
@@ -2256,7 +2471,7 @@ public class Plot implements Cloneable {
 			int xMinus = scaleXWithOverflow(x[i] - e[i]);
 			ip.moveTo(xMinus,y0);
 			ip.lineTo(xPlus, y0);
-		}		
+		}
 	}
 
 	/** Draw a polygon line; NaN values interrupt it. For reasonable performance, line segments that
@@ -2287,48 +2502,41 @@ public class Plot implements Cloneable {
 			return;
 		ip.setFont(scaledFont);
 		FontMetrics fm = ip.getFontMetrics();
-		int w =	 ip.getStringWidth(yLabel) + sc(5);
-		int h =	 fm.getHeight()+sc(1);
-		ImageProcessor label = new ByteProcessor(w, h);
-		label.setAntialiasedText(antialiasedText);
+		int h = fm.getHeight();
+		//int w =	 ip.getStringWidth(yLabel) + sc(5);
+		//int h =	 fm.getHeight()+sc(1);
+		Rectangle rect = ip.getStringBounds(yLabel);
+		ImageProcessor label = new ByteProcessor(rect.x+rect.width, Math.max(h, rect.height));
+		label.setAntialiasedText(pp.antialiasedText);
 		if (invertedLut)
 			label.invertLut();
 		label.setColor(Color.white);
 		label.fill();
 		label.setColor(Color.black);
 		label.setFont(scaledFont);
-		label.drawString(yLabel, 0, h);
+		label.drawString(yLabel, 0, Math.max(-rect.y, h)); //can't antialias if x<0 or y<h
 		label = label.rotateLeft();
-		int y2 = yFrameTop + (frameHeight-ip.getStringWidth(yLabel))/2;
-		if (y2 < yFrameTop) y2 = yFrameTop;
-		int x2 = Math.max(xRight-h, 0);
+		int y2 = yFrameTop + (frameHeight-label.getHeight())/2;
+		if (y2 < 0) y2 = 0;
+		int x2 = Math.max(xRight-label.getWidth()*4/3, 0); // distance 1/3 height if possible
 		ip.insert(label, x2, y2);
 	}
 
 	/** Draw the legend */
 	void drawLegend(PlotObject legendObject, ImageProcessor ip) {
-		ip.setFont (scFont(legendObject.font));
-		String[] labels = null;
-		if (legendObject.label != null)
-			labels = legendObject.label.split("[\t\n]");
-		int n = 0;
+		ip.setFont(scFont(legendObject.getFont()));
 		int nLabels = 0;
 		int maxStringWidth = 0;
 		float maxLineThickness = 0;
 		for (PlotObject plotObject : allPlotObjects)
-			if (plotObject.type == PlotObject.XY_DATA) {
-				if (labels != null && n < labels.length && labels[n].length()>0)
-					plotObject.label = labels[n];	// sets the label
-				if (plotObject.label != null) {		//label exists: was set now or previously
-					nLabels++;
-					int w = ip.getStringWidth(plotObject.label);
-					if (w > maxStringWidth) maxStringWidth = w;
-					if (plotObject.lineWidth > maxLineThickness) maxLineThickness = plotObject.lineWidth;
-				}
-				n++;
+			if (plotObject.type == PlotObject.XY_DATA && !plotObject.hasFlag(PlotObject.HIDDEN) && plotObject.label != null) {		//label exists: was set now or previously
+				nLabels++;
+				int w = ip.getStringWidth(plotObject.label);
+				if (w > maxStringWidth) maxStringWidth = w;
+				if (plotObject.lineWidth > maxLineThickness) maxLineThickness = plotObject.lineWidth;
 			}
 		if (nLabels == 0) return;
-		if (antialiasedText && scale > 1)		//fix incorrect width of large fonts
+		if (pp.antialiasedText && scale > 1)		//fix incorrect width of large fonts
 			maxStringWidth = (int)((1 + 0.004*scale) * maxStringWidth);
 		int frameThickness = sc(legendObject.lineWidth > 0 ? legendObject.lineWidth : 1);
 		FontMetrics fm = ip.getFontMetrics();
@@ -2364,7 +2572,7 @@ public class Plot implements Cloneable {
 		int xText = x0 + frameThickness/2 + sc(2f*LEGEND_PADDING + LEGEND_LINELENGTH + maxLineThickness);
 		int xMarker = x0 + frameThickness/2 + sc(LEGEND_PADDING + 0.5f*(LEGEND_LINELENGTH + maxLineThickness));
 		for (PlotObject plotObject : allPlotObjects)
-			if (plotObject.type == PlotObject.XY_DATA && plotObject.label != null) {
+			if (plotObject.type == PlotObject.XY_DATA && !plotObject.hasFlag(PlotObject.HIDDEN) && plotObject.label != null) {		//label exists: was set now or previously
 				if (plotObject.hasFilledMarker()) {
 					ip.setColor(plotObject.color2);
 					fillShape(plotObject.shape, xMarker, y, plotObject.getMarkerSize());
@@ -2504,7 +2712,7 @@ public class Plot implements Cloneable {
 	public void setPlotMaker(PlotMaker plotMaker) {
 		this.plotMaker = plotMaker;
 	}
-	
+
 	PlotMaker getPlotMaker() {
 		return plotMaker;
 	}
@@ -2515,7 +2723,7 @@ public class Plot implements Cloneable {
 		String labels = "";
 		boolean first = true;
 		for (PlotObject plotObject : allPlotObjects)
-			if (plotObject.type == PlotObject.XY_DATA) {
+			if (plotObject.type == PlotObject.XY_DATA && !plotObject.hasFlag(PlotObject.HIDDEN)) {
 				if (first)
 					first = false;
 				else
@@ -2530,9 +2738,11 @@ public class Plot implements Cloneable {
 		return getResultsTable(true);
 	}
 
-	/** Creates a ResultsTable with the data of the plot. Returns an empty table if no data. 
-	 *	Does not write the first x column if writeFirstXColumn is false.
-	 *	x columns equal to the first x column are never written, independent of writeFirstXColumn */
+	/** Creates a ResultsTable with the data of the plot. Returns an empty table if no data.
+	 * Does not write the first x column if writeFirstXColumn is false.
+	 * When all columns are the same length, x columns equal to the first x column are
+	 * not written, independent of writeFirstXColumn.
+	 */
 	public ResultsTable getResultsTable(boolean writeFirstXColumn) {
 		ResultsTable rt = new ResultsTable();
 		rt.showRowNumbers(false);
@@ -2544,20 +2754,34 @@ public class Plot implements Cloneable {
 				nDataSets++;
 				tableLength = Math.max(tableLength, plotObject.xValues.length);
 			}
-		if (nDataSets == 0) return null;
+		if (nDataSets == 0)
+			return null;
 		// enter columns one by one to lists of data and headings
 		ArrayList<String> headings = new ArrayList<String>(2*nDataSets);
 		ArrayList<float[]> data = new ArrayList<float[]>(2*nDataSets);
 		int dataSetNumber = 0;
 		int arrowsNumber = 0;
 		PlotObject firstXYobject = null;
+		boolean allSameLength = true;
 		for (PlotObject plotObject : allPlotObjects) {
 			if (plotObject.type==PlotObject.XY_DATA) {
-				boolean sameX =	 firstXYobject != null && Arrays.equals(firstXYobject.xValues, plotObject.xValues);
+				if (firstXYobject != null && firstXYobject.xValues.length!=plotObject.xValues.length) {
+					allSameLength = false;
+					break;
+				}
+				if (firstXYobject==null)
+					firstXYobject = plotObject;
+			}
+		}
+		firstXYobject = null;
+		for (PlotObject plotObject : allPlotObjects) {
+			if (plotObject.type==PlotObject.XY_DATA) {
+				boolean sameX = firstXYobject!=null && Arrays.equals(firstXYobject.xValues, plotObject.xValues) && allSameLength;
 				boolean sameXY = sameX && Arrays.equals(firstXYobject.yValues, plotObject.yValues); //ignore duplicates (e.g. Markers plus Curve)
 				boolean writeX = firstXYobject==null?writeFirstXColumn:!sameX;
 				addToLists(headings, data, plotObject, dataSetNumber, writeX, /*writeY=*/!sameXY, nDataSets>1);
-				if (firstXYobject == null) firstXYobject = plotObject;
+				if (firstXYobject == null)
+					firstXYobject = plotObject;
 				dataSetNumber++;
 			} else if (plotObject.type==PlotObject.ARROWS) {
 				addToLists(headings, data, plotObject, arrowsNumber, /*writeX=*/true, /*writeY=*/true, nDataSets>1);
@@ -2580,7 +2804,6 @@ public class Plot implements Cloneable {
 		nColumns = rt.getLastColumn() + 1;
 		for (int i=0; i<nColumns; i++)
 			rt.setDecimalPlaces(i, getPrecision(rt.getColumn(i)));
-
 		return rt;
 	}
 
@@ -2648,26 +2871,77 @@ public class Plot implements Cloneable {
 
 	/** Whether a given flag 'what' is set */
 	boolean hasFlag(int what) {
-		return (flags&what) != 0;
+		return (pp.axisFlags&what) != 0;
 	}
 }
 
-/** This class contains the data and properties for displaying a curve, a set of arrows, a line or a label in a plot
- *	Note that all properties such as lineWidths and Fonts have to be scaled up for high-resolution plots. */
-class PlotObject implements Cloneable {
+/** This class contains the properties of the plot, such as size, format, range, etc, except for the data+format (plot contents) */
+class PlotProperties implements Cloneable, Serializable {
+	/** The serialVersionUID should not be modified, otherwise saved plots won't be readable any more */
+	static final long serialVersionUID = 1L;
+	//
+	PlotObject frame = new PlotObject(Plot.DEFAULT_FRAME_LINE_WIDTH);	 //the frame, including background color and axis numbering
+	PlotObject xLabel = new PlotObject(PlotObject.AXIS_LABEL);		//the x axis label (string & font)
+	PlotObject yLabel = new PlotObject(PlotObject.AXIS_LABEL);		//the x axis label (string & font)
+	PlotObject legend;												//the legend (if any)
+	int width = 0;													//canvas width (note: when stored, this must fit the image)
+	int height = 0;
+	int axisFlags;													//these define axis layout
+	double[] rangeMinMax;											//currentMinMax when writing, sets defaultMinMax when reading
+	boolean antialiasedText = true;
+	boolean isFrozen;							                    //modifications (size, range, contents) don't update the ImageProcessor
+
+	/** Returns an array of all PlotObjects defined as PlotProperties. Note that some may be null */
+	PlotObject[] getAllPlotObjects() {
+		return new PlotObject[]{frame, xLabel, xLabel, legend};
+	}
+
+	/** Returns the PlotObject for xLabel ('x'), yLabel('y'), frame ('f'; includes number font) or the legend ('l'). */
+	PlotObject getPlotObject(char c) {
+		switch(c) {
+			case 'x':  return xLabel;
+			case 'y':  return yLabel;
+			case 'f':  return frame;
+			case 'l':  return legend;
+			default:   return null;
+		}
+	}
+
+	/** A shallow clone that does not duplicate arrays or objects */
+	public PlotProperties clone() {
+		try {
+			return (PlotProperties)(super.clone());
+		} catch (CloneNotSupportedException e) {
+			return null;
+		}
+	}
+
+}
+
+/** This class contains the data and properties for displaying a curve, a set of arrows, a line or a label in a plot,
+ *	as well as the legend, axis labels, and frame (including background and fonts of axis numbering).
+ *	Note that all properties such as lineWidths and Fonts have to be scaled up for high-resolution plots.
+ *	This class allows serialization for writing into tiff files */
+class PlotObject implements Cloneable, Serializable {
+	/** The serialVersionUID should not be modified, otherwise saved plots won't be readable any more */
+	static final long serialVersionUID = 1L;
 	/** constants for the type of objects */
 	public final static int XY_DATA = 0, ARROWS = 1, LINE = 2, NORMALIZED_LINE = 3, DOTTED_LINE = 4,
-			LABEL = 5, NORMALIZED_LABEL = 6, LEGEND = 7;
-	/** flags */
-	public final static int CONSTRUCTOR_DATA = 1;
-	/** if true, the object is a label, o */
+			LABEL = 5, NORMALIZED_LABEL = 6, LEGEND = 7, AXIS_LABEL = 8, FRAME = 9;
+	/** mask for recovering font style from the flags */
+	final static int FONT_STYLE_MASK = 0x0f;
+	/** flag for the data set passed with the constructor. Note that 0 to 0x0f are reserved for fonts modifiers, 0x010-0x800 are reserved for legend modifiers */
+	public final static int CONSTRUCTOR_DATA = 0x1000;
+	/** flag for hiding a PlotObject */
+	public final static int HIDDEN = 0x2000;
+	/** Type of the object; XY_DATA stands for curve or markers, can be also ARROWS ... LEGEND */
 	public int type = XY_DATA;
 	/** bitwise combination of flags, or the position of a legend */
 	public int flags;
-	/** The x and y data arrays, the error bars. These arrays also serve as x0, y0, x1, y1
+	/** The x and y data arrays and the error bars (if non-null). These arrays also serve as x0, y0, x1, y1
 	 *	arrays for plotting arrays of arrows */
 	public float[] xValues, yValues, xEValues, yEValues;
-	/** Type of the points, such as Plot.LINE, Plot.CROSS etc. */
+	/** Type of the points, such as Plot.LINE, Plot.CROSS etc. (for type = XY_DATA) */
 	public int shape;
 	/** The line width in pixels for 'small' plots */
 	public float lineWidth;
@@ -2677,7 +2951,7 @@ class PlotObject implements Cloneable {
 	public Color color2;
 	/* Labels and lines only: Position (NORMALIZED objects: relative units 0...1) */
 	public double x, y;
-	/* Lines only: End position*/
+	/* Lines only: End position */
 	public double xEnd, yEnd;
 	/* Dotted lines only: step */
 	public int step;
@@ -2685,8 +2959,17 @@ class PlotObject implements Cloneable {
 	public String label;
 	/** Labels only: Justification can be Plot.LEFT, Plot.CENTER or Plot.RIGHT */
 	public int justification;
-	/** Labels only: the font; maybe null for default */
-	public Font font;
+	/** Text objects (labels, legend, axis labels) only: the font; maybe null for default. This is not serialized (transient) */
+	private transient Font font;
+	/** String for representation of the font family (for Serialization); may be null for default. Font style is in flags, font size in fontSize. */
+	private String fontFamily;
+	/** Font size (for Serialization) */
+	private float fontSize;
+
+	/** Generic constructor */
+	PlotObject(int type) {
+		this.type = type;
+	}
 
 	/** Constructor for XY_DATA, i.e., a curve or set of points */
 	PlotObject(float[] xValues, float[] yValues, float[] yErrorBars, int shape, float lineWidth, Color color, Color color2, String yLabel) {
@@ -2731,25 +3014,42 @@ class PlotObject implements Cloneable {
 		this.x = x;
 		this.y = y;
 		this.justification = justification;
-		this.font = font;
+		setFont(font);
 		this.color = color;
 	}
 
-	/** Constructor for the legend. <code>flags</code> is bitwise or of TOP_LEFT etc. and LEGEND_TRANSPARENT if desired */
-	PlotObject(String labels, float lineWidth, Font font, Color color, int flags) {
+	/** Constructor for the legend. <code>flags</code> is bitwise or of TOP_LEFT etc. and LEGEND_TRANSPARENT if desired.
+	 *  Note that the labels in the legend are those of the data plotObjects */
+	PlotObject(float lineWidth, Font font, Color color, int flags) {
 		this.type = LEGEND;
-		this.label = labels;
 		this.lineWidth = lineWidth;
-		this.font = font;
+		setFont(font);
 		this.color = color;
 		this.flags = flags;
 	}
 
+	/** Constructor for the frame, including axis numbers. In the current version, the primary color (line color) is always black */
+	PlotObject(float lineWidth) {
+		this.type = FRAME;
+		this.color = Color.black;
+		this.lineWidth = lineWidth;
+	}
+
 	/** Whether a given flag 'what' is set */
 	boolean hasFlag(int what) {
 		return (flags&what) != 0;
 	}
 
+	/** Sets a given flag  */
+	void setFlag(int what) {
+		flags |= what;
+	}
+
+	/** Unsets a given flag  */
+	void unsetFlag(int what) {
+		flags = flags & (~what);
+	}
+
 	/** Whether an XY_DATA object has a curve to draw */
 	boolean hasCurve() {
 		return type == XY_DATA && (shape == Plot.LINE || shape == Plot.CONNECTED_CIRCLES);
@@ -2771,7 +3071,27 @@ class PlotObject implements Cloneable {
 		return lineWidth<=1 ? 5 : 7;
 	}
 
-	/** A shallow clone and does not duplicate arrays*/
+	/** Sets the font. Also writes font properties for serialization. */
+	void setFont(Font font) {
+		if (font == this.font) return;
+		this.font = font;
+		if (font == null) {
+			fontFamily = null;
+		} else {
+			fontFamily = font.getFamily();
+			flags = (flags & ~FONT_STYLE_MASK) | font.getStyle();
+			fontSize = font.getSize2D();
+		}
+	}
+
+	/** Returns the font, or null if none specified */
+	Font getFont() {
+		if (font == null && fontFamily != null)     //after recovery from serialization, create the font from its description
+			font = FontUtil.getFont(fontFamily, flags&FONT_STYLE_MASK, fontSize);
+		return font;
+	}
+
+	/** A shallow clone that does not duplicate arrays or objects */
 	public PlotObject clone() {
 		try {
 			return (PlotObject)(super.clone());
@@ -2779,4 +3099,4 @@ class PlotObject implements Cloneable {
 			return null;
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/ij/gui/PlotCanvas.java b/ij/gui/PlotCanvas.java
index a670448..2033de4 100644
--- a/ij/gui/PlotCanvas.java
+++ b/ij/gui/PlotCanvas.java
@@ -10,7 +10,7 @@ import java.awt.event.*;
 /** This subclass of ImageCanvas has special provisions for plots:
  * - Zooming: sets the plot range
  * - Scrolling: moves data area
- *  This behavior can be suppressed if 
+ *  This behavior is suppressed if the plot is frozen
  * */
 public class PlotCanvas extends ImageCanvas {
 	/** The plot displayed */
@@ -147,20 +147,20 @@ public class PlotCanvas extends ImageCanvas {
 	}
 
 	/** Resizes the canvas when the user resizes the window. To avoid a race condition while creating
-	 *	a new window, this is ignored if no window exists or the window layout is not finished */
+	 *	a new window, this is ignored if no window exists or the window has not been activated yet.
+     */
 	void resizeCanvas(int width, int height) {
 		if (plot == null || plot.isFrozen()) {
 			super.resizeCanvas(width, height);
 			return;
 		}
 		resetMagnification();
-		//IJ.log("resizeCanvas "+width+"x"+height);
 		if (width == oldWidth && height == oldHeight) return;
 		if (plot == null) return;
 		ImageWindow win = imp.getWindow();
 		if (win==null || !(win instanceof PlotWindow)) return;
-		if (!((PlotWindow)win).layoutDone) return;				// window layout not finished yet?
-		((PlotWindow)win).updateMinimumSize();
+		if (!win.isVisible()) return;
+		if (!((PlotWindow)win).wasActivated) return;				// window layout not finished yet?
 		Dimension minSize = plot.getMinimumSize();
 		int plotWidth  =  width < minSize.width	 ? minSize.width  : width;
 		int plotHeight = height < minSize.height ? minSize.height : height;
diff --git a/ij/gui/PlotDialog.java b/ij/gui/PlotDialog.java
index ba4b9cd..b5e4be7 100644
--- a/ij/gui/PlotDialog.java
+++ b/ij/gui/PlotDialog.java
@@ -5,7 +5,8 @@ import ij.plugin.frame.Recorder;
 import java.awt.*;
 
 /*
- * This class contains dialog for formatting of plots
+ * This class contains dialogs for formatting of plots (range, axes, labels, legend, creating a high-resolution plot)
+ * Formatting of contents (curves, symbols, ...) is in PlotContentsStyleDialog
  */
 
 public class PlotDialog {
@@ -32,7 +33,7 @@ public class PlotDialog {
 	//saved dialog options: High-resolution plot
 	private static float hiResFactor = 4.0f;
 	private static boolean hiResAntiAliased = true;
-	
+
 	/** Construct a new PlotDialog, show it and do the appropriate action on the plot
 	 */
 	public PlotDialog(Plot plot, int dialogType) {
@@ -84,7 +85,7 @@ public class PlotDialog {
 			currentMinMax[1] = linXMax;
 			currentMinMax[2] = linYMin;
 			currentMinMax[3] = linYMax;
-			
+
 			if (livePlot) plot.templateFlags = setFlag(plot.templateFlags, Plot.X_RANGE, gd.getNextBoolean());
 			boolean xLog = gd.getNextBoolean();
 			if (livePlot) plot.templateFlags = setFlag(plot.templateFlags, Plot.Y_RANGE, gd.getNextBoolean());
@@ -122,7 +123,7 @@ public class PlotDialog {
 			panel.add(new Label("X Axis"), c);
 			c.gridx = 2;
 			panel.add(new Label("Y Axis"), c);
-			
+
 			Checkbox[] checkboxes = new Checkbox[labels.length*columns];
 			for (int l=0; l<labels.length; l++) {
 				c.gridy++;
@@ -139,13 +140,24 @@ public class PlotDialog {
 			gd.addPanel(panel);
 			gd.setInsets(15, 0, 3); // top, left, bottom -- extra space
 			Font plotFont = (plot.currentFont != null) ? plot.currentFont : plot.defaultFont;
-			Font labelFont = (plot.xLabelFont != null) ? plot.xLabelFont : plotFont;
-			gd.addNumericField("Number Font Size", plotFont.getSize2D(), 1);
+			Font labelFont = plot.getFont('x');
+			if (labelFont == null) labelFont = plotFont;
+			Font numberFont = plot.getFont('f');
+			if (numberFont == null) numberFont = plotFont;
+			String plotXLabel = plot.getLabel('x');
+			String plotYLabel = plot.getLabel('y');
+			if (plotXLabel == null) plotXLabel = "";
+			if (plotYLabel == null) plotYLabel = "";
+			if (xLabel == null || (!(plotXLabel.equals("Distance (pixels)") || plotXLabel.equals("Distance ( )"))))
+				xLabel = plotXLabel;
+			if (yLabel == null || !plotYLabel.equals("Gray Value"))
+                yLabel = plotYLabel; // suggest last dialog entry if default profile label
+			gd.addNumericField("Number Font Size", numberFont.getSize2D(), 1);
 			gd.addNumericField("Label Font Size", labelFont.getSize2D(), 1);
-			gd.addStringField("X Axis Label", xLabel != null ? xLabel : plot.xLabel, 15);
-			gd.addStringField("Y Axis Label", yLabel != null ? yLabel : plot.yLabel, 15);
+			gd.addStringField("X Axis Label", xLabel, 15);
+			gd.addStringField("Y Axis Label", yLabel, 15);
 			gd.setInsets(0, 20, 0); // no extra space
-			gd.addCheckbox("Bold Labels", plotFontSize>0 ? axisLabelBold : (plotFont.isBold()));
+			gd.addCheckbox("Bold Labels", plotFontSize>0 ? axisLabelBold : (labelFont.isBold()));
 			gd.showDialog();
 			if (gd.wasCanceled()) return;
 
@@ -155,14 +167,16 @@ public class PlotDialog {
 				flags = setFlag(flags, xFlags[l]<<1, checkboxes[l*columns+1].getState());
 			}
 			plot.setFormatFlags(flags);
-			float plotFontSize = (float)gd.getNextNumber();
-			if (gd.invalidNumber()) plotFontSize = plotFont.getSize2D();
+			float numberFontSize = (float)gd.getNextNumber();
+			if (gd.invalidNumber()) numberFontSize = numberFont.getSize2D();
+			if (numberFontSize < 9)  numberFontSize = 9f;
+			if (numberFontSize > 24) numberFontSize = 24f;
 			float labelFontSize = (float)gd.getNextNumber();
 			if (gd.invalidNumber()) labelFontSize = labelFont.getSize2D();
 			xLabel = gd.getNextString();
 			yLabel = gd.getNextString();
 			axisLabelBold = gd.getNextBoolean();
-			plot.setFont(-1, plotFontSize);
+			plot.setFont('f', numberFont.deriveFont(numberFont.getStyle(), numberFontSize));
 			plot.setAxisLabelFont(axisLabelBold ? Font.BOLD : Font.PLAIN, labelFontSize);
 			plot.setXYLabels(xLabel, yLabel);
 			plot.updateImage();
diff --git a/ij/gui/PlotWindow.java b/ij/gui/PlotWindow.java
index 182d281..1575d32 100644
--- a/ij/gui/PlotWindow.java
+++ b/ij/gui/PlotWindow.java
@@ -58,7 +58,7 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 	 *	only min&max value of the axes are given */
 	public static boolean noTicks;
 
-	
+
 	private static final String PREFS_WIDTH = "pp.width";
 	private static final String PREFS_HEIGHT = "pp.height";
 	private static final String PREFS_FONT_SIZE = "pp.fontsize";
@@ -71,6 +71,8 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 	private static final int NO_TICKS = 32;
 	private static String moreButtonLabel = "More "+'\u00bb';
 
+	boolean wasActivated;			// true after window has been activated once, needed by PlotCanvas
+
 	private Button list, save, more, live;
 	private PopupMenu popupMenu;
 	private MenuItem[] menuItems;
@@ -81,7 +83,6 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 	private int markSize = 5;
 	private static Plot staticPlot;
 	private Plot plot;
-	boolean layoutDone;				// becomes true after the layout has been done, used by PlotCanvas
 	private String blankLabel = "                       ";
 
 	private PlotMaker plotMaker;
@@ -92,7 +93,6 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 	private Roi[] rangeArrowRois;	// these constitute the arrow overlays for changing the range
 	private boolean rangeArrowsVisible;
 	private int activeRangeArrow = -1;
-	
 
 	// static initializer
 	static {
@@ -103,8 +103,8 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 		plotHeight = Prefs.getInt(PREFS_HEIGHT, HEIGHT);
 		fontSize = Prefs.getInt(PREFS_FONT_SIZE, FONT_SIZE);
 		interpolate = (options&INTERPOLATE)==0; // 0=true, 1=false
-		noGridLines = (options&NO_GRID_LINES)!=0; 
-		noTicks = (options&NO_TICKS)!=0; 
+		noGridLines = (options&NO_GRID_LINES)!=0;
+		noTicks = (options&NO_TICKS)!=0;
 	}
 
 	/**
@@ -124,7 +124,16 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 	public PlotWindow(String title, String xLabel, String yLabel, double[] xValues, double[] yValues) {
 		this(title, xLabel, yLabel, Tools.toFloat(xValues), Tools.toFloat(yValues));
 	}
-	
+
+	/** Creates a PlotWindow from a given ImagePlus with a Plot object.
+	 *  (called when reading an ImagePlus with an associated plot from a file) */
+	public PlotWindow(ImagePlus imp, Plot plot) {
+		super(imp);
+		((PlotCanvas)getCanvas()).setPlot(plot);
+		this.plot = plot;
+		draw();
+	}
+
 	/** Creates a PlotWindow from a Plot object. */
 	PlotWindow(Plot plot) {
 		super(plot.getImagePlus());
@@ -139,63 +148,67 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 		staticPlot = new Plot(title, xLabel, yLabel, xValues, yValues);
 		return new ImagePlus(title, staticPlot.getBlankProcessor());
 	}
-	
+
 	/** Sets the x-axis and y-axis range.
-		Must be called before the plot is displayed. */
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void setLimits(double xMin, double xMax, double yMin, double yMax) {
 		plot.setLimits(xMin, xMax, yMin, yMax);
 	}
 
 	/** Adds a set of points to the plot or adds a curve if shape is set to LINE.
-		Must be called before the plot is displayed.
 	 *	Note that there are more options available by using the methods of the Plot class instead.
-	 * @param x			the x-coodinates
-	 * @param y			the y-coodinates
-	 * @param shape		Plot.CIRCLE, X, BOX, TRIANGLE, CROSS, LINE etc. */
-
+	 *  @param x			the x-coodinates
+	 *  @param y			the y-coodinates
+	 *  @param shape		Plot.CIRCLE, X, BOX, TRIANGLE, CROSS, LINE etc.
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void addPoints(float[] x, float[] y, int shape) {
 		plot.addPoints(x, y, shape);
 	}
 
 	/** Adds a set of points to the plot using double arrays.
-		Must be called before the plot is displayed.
-		Note that there are more options available by using the methods of the Plot class instead. */
+	 *	Must be called before the plot is displayed.
+	 *	Note that there are more options available by using the methods of the Plot class instead.
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void addPoints(double[] x, double[] y, int shape) {
 		addPoints(Tools.toFloat(x), Tools.toFloat(y), shape);
 	}
-	
+
 	/** Adds vertical error bars to the plot.
-		Must be called before the plot is displayed.
-		Note that there are more options available by using the methods of the Plot class instead. */
+	 *	Must be called before the plot is displayed.
+	 *	Note that there are more options available by using the methods of the Plot class instead.
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void addErrorBars(float[] errorBars) {
 		plot.addErrorBars(errorBars);
 	}
 
 	/** Draws a label.
-		Must be called before the plot is displayed.
-		Note that there are more options available by using the methods of the Plot class instead. */
+	 *	Note that there are more options available by using the methods of the Plot class instead.
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void addLabel(double x, double y, String label) {
 		plot.addLabel(x, y, label);
 	}
-	
+
 	/** Changes the drawing color. The frame and labels are
-		always drawn in black.
-		Must be called before the plot is displayed.
-		Note that there are more options available by using the methods of the Plot class instead. */
+	 *	always drawn in black.
+	 *	Must be called before the plot is displayed.
+	 *	Note that there are more options available by using the methods of the Plot class instead.
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void setColor(Color c) {
 		plot.setColor(c);
 	}
 
 	/** Changes the line width.
-		Must be called before the plot is displayed.
-		Note that there are more options available by using the methods of the Plot class instead. */
+	 *	Must be called before the plot is displayed.
+	 *	Note that there are more options available by using the methods of the Plot class instead.
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void setLineWidth(int lineWidth) {
 		plot.setLineWidth(lineWidth);
 	}
 
 	/** Changes the font.
-		Must be called before the plot is displayed.
-		Note that there are more options available by using the methods of the Plot class instead. */
+	 *	Must be called before the plot is displayed.
+	 *	Note that there are more options available by using the methods of the Plot class instead.
+	 *  @deprecated use the corresponding method of the Plot class */
 	public void changeFont(Font font) {
 		plot.changeFont(font);
 	}
@@ -233,28 +246,51 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 		pack();
 
 		ImageProcessor ip = plot.getProcessor();
-		if ((ip instanceof ColorProcessor) && (imp.getProcessor() instanceof ByteProcessor))
+		boolean ipIsColor = ip instanceof ColorProcessor;
+		boolean impIsColor = imp.getProcessor() instanceof ColorProcessor;
+		if (ipIsColor != impIsColor)
 			imp.setProcessor(null, ip);
 		else
 			imp.updateAndDraw();
-		layoutDone = true;
 		if (listValues)
 			showList();
 		else
 			ic.requestFocus();	//have focus on the canvas, not the button, so that pressing the space bar allows panning
 	}
 
+	/** Sets the Plot object shown in this PlotWindow. Does not update the window. */
+	public void setPlot(Plot plot) {
+		this.plot = plot;
+		((PlotCanvas)getCanvas()).setPlot(plot);
+	}
+
 	/** Releases the resources used by this PlotWindow */
 	public void dispose() {
 		if (plot!=null)
 			plot.dispose();
-		removeListeners();
+		disableLivePlot();
 		plot = null;
 		plotMaker = null;
 		srcImp = null;
 		super.dispose();
 	}
 
+	/** Called when the window is activated (WindowListener)
+	 *  Window layout is finished at latest a few millisec after windowActivated, then the
+     *  'wasActivated' boolean is set to tell the ImageCanvas that resize events should
+     *  lead to resizing the canvas (before, creating the layout can lead to resize events)*/
+	public void windowActivated(WindowEvent e) {
+		super.windowActivated(e);
+		if (!wasActivated) {
+			new Thread(new Runnable() {
+				public void run() {
+					IJ.wait(50);  //sometimes, window layout is done only a few millisec after windowActivated
+					wasActivated = true;
+				}
+            }).start();
+		}
+	}
+
 	/** Called when the canvas is resized */
 	void updateMinimumSize() {
 		if (plot == null) return;
@@ -370,14 +406,13 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 		}
 	}
 
-//n__ begin mouseMoved
-    /**
-     * Updates the X and Y values when the mouse is moved and, if appropriate,
-     * shows/hides the overlay with the triangular buttons for changing the axis
-     * range limits Overrides mouseMoved() in ImageWindow.
-     *
-     * @see ij.gui.ImageWindow#mouseMoved
-     */
+	/**
+	 * Updates the X and Y values when the mouse is moved and, if appropriate,
+	 * shows/hides the overlay with the triangular buttons for changing the axis
+	 * range limits Overrides mouseMoved() in ImageWindow.
+	 *
+	 * @see ij.gui.ImageWindow#mouseMoved
+	 */
     public void mouseMoved(int x, int y) {
         super.mouseMoved(x, y);
         if (plot == null)
@@ -409,8 +444,7 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
             }
         } else if (rangeArrowsVisible)
             hideRangeArrows();
-    }    
-    //n__ end mouseMoved
+    }
 
 	/** Called by PlotCanvas */
 	void mouseExited(MouseEvent e) {
@@ -442,7 +476,6 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 			plot.scroll(0, rotation*amount*Math.max(ic.imageHeight/50, 1));
 	}
 
-    //n__ begin showRangeArrows
     /**
      * Creates an overlay with triangular buttons for changing the axis range
      * limits and shows it
@@ -451,7 +484,7 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
         if (imp == null)
             return;
         hideRangeArrows(); //in case we have old arrows from a different plot size or so
-        rangeArrowRois = new Roi[4 * 2 + 1]; //4 arrows per axis plus 1 'Reset' icon 
+        rangeArrowRois = new Roi[4 * 2 + 1]; //4 arrows per axis plus 1 'Reset' icon
         int i = 0;
         int height = imp.getHeight();
         int arrowH = plot.topMargin < 14 ? 6 : 8; //height of arrows and distance between them; base is twice that value
@@ -490,8 +523,7 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
         ic.repaint();
         rangeArrowsVisible = true;
     }
-    //n__ end showRangeArrows
-          
+
 	void hideRangeArrows() {
 		if (imp == null || rangeArrowRois==null) return;
 		Overlay ovly = imp.getOverlay();
@@ -524,7 +556,7 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 			close();
 		}
 	}
-	
+
 	/** Returns the plot values as a ResultsTable. */
 	public ResultsTable getResultsTable() {
 		return plot.getResultsTable(saveXValues);
@@ -606,7 +638,7 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 		if (autoClose)
 			{imp.changes=false; close();}
 	}
-		
+
 	public void lostOwnership(Clipboard clipboard, Transferable contents) {}
 
 	public float[] getXValues() {
@@ -641,46 +673,59 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 		if (autoClose && !listValues) options |= AUTO_CLOSE;
 		if (listValues) options |= LIST_VALUES;
 		if (!interpolate) options |= INTERPOLATE; // true=0, false=1
-		if (noGridLines) options |= NO_GRID_LINES; 
-		if (noTicks) options |= NO_TICKS; 
+		if (noGridLines) options |= NO_GRID_LINES;
+		if (noTicks) options |= NO_TICKS;
 		prefs.put(OPTIONS, Integer.toString(options));
 	}
 
 	private void toggleLiveProfiling() {
-		boolean liveMode = live.getForeground()==Color.red;
+		boolean liveMode = bgThread != null;
 		if (liveMode)
-			removeListeners();
+			disableLivePlot();
 		else
-			enableLiveProfiling();
+			enableLivePlot();
 	}
-	
-	/*
-	public static void setLiveMode(Boolean set) {
-		ImagePlus imp = WindowManager.getCurrentImage();
-		ImageWindow win = imp!=null?imp.getWindow():null;
-		PlotWindow pw = win!=null && (win instanceof PlotWindow)?(PlotWindow)win:null;
-		if (pw!=null && pw.live.getForeground()!=Color.red)
-			pw.enableLiveProfiling();
-	}
-	*/
 
-	private void enableLiveProfiling() {
+	/* Enable live plotting.
+	 * This requires that the PlotWindow has been initialized with a Plot having a PlotMaker */
+	private void enableLivePlot() {
 		if (plotMaker==null)
 			plotMaker = plot!=null?plot.getPlotMaker():null;
-		if (plotMaker!=null && bgThread==null) {
-			srcImp = plotMaker.getSourceImage();
-			if (srcImp==null)
-				return;
-			bgThread = new Thread(this, "Live Profiler");
+		if (plotMaker==null) return;
+		srcImp = plotMaker.getSourceImage();
+		if (srcImp==null)
+			return;
+		if (bgThread==null) {
+			bgThread = new Thread(this, "Live Plot");
 			bgThread.setPriority(Math.max(bgThread.getPriority()-3, Thread.MIN_PRIORITY));
+			doUpdate = true;
 			bgThread.start();
-			imageUpdated(srcImp);
 		}
-		createListeners();
-		if (srcImp!=null)
-			imageUpdated(srcImp);
+		if (IJ.debugMode) IJ.log("PlotWindow.createListeners");
+		ImagePlus.addImageListener(this);
+		Roi.addRoiListener(this);
+		Font font = live.getFont();
+		live.setFont(new Font(font.getName(), Font.BOLD, font.getSize()));
+		live.setForeground(Color.red);
+	}
+
+	private void disableLivePlot() {
+		if (IJ.debugMode) IJ.log("PlotWindow.disableLivePlot: "+srcImp);
+		if (srcImp==null)
+			return;
+		if (bgThread!=null)
+			bgThread.interrupt();
+		bgThread = null;
+		ImagePlus.removeImageListener(this);
+		Roi.removeRoiListener(this);
+		if (live != null) {
+			Font font = live.getFont();
+			live.setFont(new Font(font.getName(), Font.PLAIN, font.getSize()));
+			live.setForeground(Color.black);
+		}
 	}
 
+
 	/** For live plots, update the plot if the ROI of the source image changes */
 	public synchronized void roiModified(ImagePlus img, int id) {
 		if (IJ.debugMode) IJ.log("PlotWindow.roiModified: "+img+"  "+id);
@@ -689,31 +734,28 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 			notify();
 		}
 	}
-	
+
 	// Unused
 	public void imageOpened(ImagePlus imp) {
 	}
 
-	/** For live plots, this method is called if the source image content is changed */
+	/** For live plots, this method is called if the source image content is changed. */
 	public synchronized void imageUpdated(ImagePlus imp) {
-		if (imp==srcImp) { 
+		if (imp==srcImp) {
 			doUpdate = true;
 			notify();
 		}
 	}
-	
-	// For live plots, if either the source image or this image are closed, exit
+
+	/** For live plots, if either the source image or this image are closed, exit live mode */
 	public void imageClosed(ImagePlus imp) {
 		if (imp==srcImp || imp==this.imp) {
-			if (bgThread!=null)
-				bgThread.interrupt();
-			bgThread = null;
-			removeListeners();
+			disableLivePlot();
 			srcImp = null;
 			plotMaker = null;
 		}
 	}
-	
+
 	// the background thread for live plotting.
 	public void run() {
 		while (true) {
@@ -742,40 +784,19 @@ public class PlotWindow extends ImageWindow implements ActionListener, ItemListe
 			}
 		}
 	}
-		
-	private void createListeners() {
-		if (IJ.debugMode) IJ.log("PlotWindow.createListeners");
-		if (srcImp==null)
-			return;
-		ImagePlus.addImageListener(this);
-		Roi.addRoiListener(this);
-		Font font = live.getFont();
-		live.setFont(new Font(font.getName(), Font.BOLD, font.getSize()));
-		live.setForeground(Color.red);
-	}
-	
-	private void removeListeners() {
-		if (IJ.debugMode) IJ.log("PlotWindow.removeListeners: "+srcImp);
-		if (srcImp==null)
-			return;
-		ImagePlus.removeImageListener(this);
-		Roi.removeRoiListener(this);
-		Font font = live.getFont();
-		live.setFont(new Font(font.getName(), Font.PLAIN, font.getSize()));
-		live.setForeground(Color.black);
-	}
-	
+
 	/** Returns the Plot associated with this PlotWindow. */
 	public Plot getPlot() {
 		return plot;
 	}
-	
-	/** Freezes the active plot window. */
+
+	/** Freezes the active plot window, so the image does not get redrawn for zooming,
+	 *  setting the range, etc. */
 	public static void freeze() {
 		Window win = WindowManager.getActiveWindow();
 		if (win!=null && (win instanceof PlotWindow))
 			((PlotWindow)win).getPlot().setFrozen(true);
 	}
-	
+
 }
 
diff --git a/ij/gui/PointRoi.java b/ij/gui/PointRoi.java
index d9ed01c..8575420 100644
--- a/ij/gui/PointRoi.java
+++ b/ij/gui/PointRoi.java
@@ -11,7 +11,7 @@ import java.awt.*;
 import java.awt.image.*;
 import java.awt.event.KeyEvent;
 import java.util.*;
-import java.awt.geom.Point2D;
+import java.awt.geom.*;
 
 /** This class represents a collection of points. */
 public class PointRoi extends PolygonRoi {
@@ -43,7 +43,6 @@ public class PointRoi extends PolygonRoi {
 	private int[] counts = new int[MAX_COUNTERS];
 	private ResultsTable rt;
 	private long lastPointTime;
-	private double scale;
 	private int[] counterInfo;
 	
 	static {
@@ -147,17 +146,12 @@ public class PointRoi extends PolygonRoi {
 	/** Draws the points on the image. */
 	public void draw(Graphics g) {
 		updatePolygon();
-		//scale = ic!=null?ic.getMagnification():1.0;
-		//if (type!=CIRCLE) scale=1.0;
-		scale = 1.0;
 		if (showLabels && nPoints>1) {
 			fontSize = 8;
 			fontSize += convertSizeToIndex(size);
 			if (fontSize>18)
 				fontSize = 18;
-			double scale2 = 0.7*scale;
-			if (scale2<1.0) scale2=1.0;
-			fontSize = (int)Math.round(fontSize*scale2);
+			fontSize = (int)Math.round(fontSize);
 			font = new Font("SansSerif", Font.PLAIN, fontSize);
 			g.setFont(font);
 			if (fontSize>9)
@@ -175,12 +169,20 @@ public class PointRoi extends PolygonRoi {
 			imp.draw();
 		}
 		PointToolOptions.update();
+		flattenScale = 1.0;
 	}
 
 	void drawPoint(Graphics g, int x, int y, int n) {
 		int size2=size/2;
 		boolean colorSet = false;
 		Graphics2D g2d = (Graphics2D)g;
+		AffineTransform saveXform = null;
+		if (flattenScale>1.0) {
+			saveXform = g2d.getTransform();
+			g2d.translate(x, y);
+			g2d.scale(flattenScale, flattenScale);
+			x = y = 0;
+		}
 		Color color = strokeColor!=null?strokeColor:ROIColor;
 		if (!overlay && isActiveOverlayRoi()) {
 			if (color==Color.cyan)
@@ -221,7 +223,7 @@ public class PointRoi extends PolygonRoi {
 				g.fillRect(x-size2, y-size2, size, size);
 		}
 		if (showLabels && nPoints>1) {
-			int offset = (int)Math.round(0.4*size*scale);
+			int offset = (int)Math.round(0.4);
 			if (offset<1) offset=1;
 			offset++;
 			if (nCounters==1) {
@@ -243,14 +245,14 @@ public class PointRoi extends PolygonRoi {
 				g.drawOval(x-(size2+1), y-(size2+1), size+1, size+1);
 		}
 		if (type==CIRCLE) {
-			int scaledSize = (int)Math.round((size+1)*scale);
+			int scaledSize = (int)Math.round(size+1);
 			g.setColor(color);
-			if (scale!=1.0)
-				g2d.setStroke(new BasicStroke((float)scale*(size>LARGE?2:1)));
-			else if (size>LARGE)
+			if (size>LARGE)
 				g2d.setStroke(twoPixelsWide);
 			g.drawOval(x-scaledSize/2, y-scaledSize/2, scaledSize, scaledSize);
 		}
+		if (saveXform!=null)
+			g2d.setTransform(saveXform);
 	}
 	
 	public void drawPixels(ImageProcessor ip) {
@@ -709,7 +711,7 @@ public class PointRoi extends PolygonRoi {
 
 	/**
 	 * Custom iterator for points contained in a {@link PointRoi}.
-	 * @author W. Burger
+	 * Author: W. Burger
 	*/
 	public Iterator<Point> iterator() {	
 		return new Iterator<Point>() {
diff --git a/ij/gui/ProfilePlot.java b/ij/gui/ProfilePlot.java
index b8cd955..ff177d1 100644
--- a/ij/gui/ProfilePlot.java
+++ b/ij/gui/ProfilePlot.java
@@ -15,8 +15,8 @@ public class ProfilePlot {
 	static final double ASPECT_RATIO = 0.5;
 	private double min, max;
 	private boolean minAndMaxCalculated;
-    private static double fixedMin;
-    private static double fixedMax;
+	private static double fixedMin;
+	private static double fixedMax;
     
 	protected ImagePlus imp;
 	protected double[] profile;
diff --git a/ij/gui/Roi.java b/ij/gui/Roi.java
index 9016006..5fbc87c 100644
--- a/ij/gui/Roi.java
+++ b/ij/gui/Roi.java
@@ -78,6 +78,8 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	protected boolean overlay;
 	protected boolean wideLine;
 	protected boolean ignoreClipRect;
+	protected double flattenScale = 1.0;
+
 	private String name;
 	private int position;
 	private int channel, slice, frame;
@@ -89,6 +91,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	private boolean isCursor;
 	private double xcenter = Double.NaN;
 	private double ycenter;
+	private boolean listenersNotified;
 
 
 	/** Creates a rectangular ROI. */
@@ -569,7 +572,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	
 	/** Returns the coordinates of the pixels inside this ROI as a FloatPolygon.
 	 * @see #getContainedPoints()
-	 * @see #Iterator()
+	 * @see #iterator()
 	 */
 	public FloatPolygon getContainedFloatPoints() {
 		Roi roi2 = this;
@@ -598,7 +601,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	 * ax, ay, bx, by: points A and B of line segment
 	 * cx, cy, rad: Circle center and radius.
 	 * ignoreOutside: if true, ignores intersections outside the line segment A-B
-	 * @Returns an array of 0, 2 or 4 coordinates (for 0, 1, or 2 intersection
+	 * Returns an array of 0, 2 or 4 coordinates (for 0, 1, or 2 intersection
 	 * points). If two intersection points are returned, they are listed in travel
 	 * direction A->B
 	 * </pre>
@@ -2121,7 +2124,16 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 			Integer.rotateRight(new Double(getYBase()).hashCode(),16);
 	}
 	
+	public void setFlattenScale(double scale) {
+		flattenScale = scale;
+	}
+	
 	public void notifyListeners(int id) {
+		if (id==RoiListener.CREATED) {
+			if (listenersNotified)
+				return;
+			listenersNotified = true;	
+		}
 		synchronized (listeners) {
 			for (int i=0; i<listeners.size(); i++) {
 				RoiListener listener = (RoiListener)listeners.elementAt(i);
@@ -2129,7 +2141,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 			}
 		}
 	}
-	
+
 	public static void addRoiListener(RoiListener listener) {
 		listeners.addElement(listener);
 	}
@@ -2139,16 +2151,16 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	}
 	
 	/**
-	 * Required by the {@link Interable} interface.
+	 * Required by the {@link Iterable} interface.
 	 * Use to iterate over the contained coordinates. Usage example: 
 	 * <pre>
 	 * for (Point p : roi) {
 	 *   // process p
 	 * }
 	 * </pre>
+	 * Author: Wilhelm Burger
 	 * @see #getContainedPoints()
 	 * @see #getContainedFloatPoints()
-	 * @author Wilhelm Burger
 	*/
 	public Iterator<Point> iterator() {
 		// Returns the default (mask-based) point iterator. Note that 'Line' overrides the 
diff --git a/ij/gui/RotatedRectRoi.java b/ij/gui/RotatedRectRoi.java
index f7147c9..36694b0 100644
--- a/ij/gui/RotatedRectRoi.java
+++ b/ij/gui/RotatedRectRoi.java
@@ -33,13 +33,18 @@ public class RotatedRectRoi extends PolygonRoi {
 		bounds = null;
 	}
 
-	public void draw(Graphics g) {
+	public void draw(Graphics g) {	
 		super.draw(g);
 		if (!overlay && ic!=null) {
 			double mag = ic.getMagnification();
 		    int size2 = HANDLE_SIZE/2;
-			for (int i=0; i<4; i++)
-				drawHandle(g, hxs(i)-size2, hys(i)-size2);
+			for (int i=0; i<4; i++){
+			if (i==3)//mark starting point
+				handleColor = strokeColor!=null?strokeColor:ROIColor;
+			else
+				handleColor=Color.white;
+			drawHandle(g, hxs(i)-size2, hys(i)-size2);
+			}
 		}
 	}
 	
@@ -110,6 +115,13 @@ public class RotatedRectRoi extends PolygonRoi {
 	protected void handleMouseUp(int screenX, int screenY) {
 		nPoints = 4;
 		state = NORMAL;
+		if (Recorder.record) {
+			double[] p = getParams();
+			if (Recorder.scriptMode())
+				Recorder.recordCall("imp.setRoi(new RotatedRectRoi("+(int)p[0]+","+(int)p[1]+","+(int)p[2]+","+(int)p[3]+","+(int)p[4]+"));");
+			else
+				Recorder.record("makeRotatedRectangle", (int)p[0], (int)p[1], (int)p[2], (int)p[3], (int)p[4]);
+		}
 	}
 	
 	protected void moveHandle(int sx, int sy) {
diff --git a/ij/gui/ShapeRoi.java b/ij/gui/ShapeRoi.java
index f3b4841..b9e39e8 100644
--- a/ij/gui/ShapeRoi.java
+++ b/ij/gui/ShapeRoi.java
@@ -7,7 +7,6 @@ import java.util.*;
 import ij.*;
 import ij.process.*;
 import ij.measure.*;
-import ij.plugin.frame.Recorder;
 import ij.plugin.filter.Analyzer;
 import ij.util.Tools;
 
diff --git a/ij/gui/StackWindow.java b/ij/gui/StackWindow.java
index 17fc374..fdca3bb 100644
--- a/ij/gui/StackWindow.java
+++ b/ij/gui/StackWindow.java
@@ -162,12 +162,13 @@ public class StackWindow extends ImageWindow implements Runnable, AdjustmentList
 			int rotation = e.getWheelRotation();
 			boolean ctrl = (e.getModifiers()&Event.CTRL_MASK)!=0;
 			if ((ctrl||IJ.shiftKeyDown()) && ic!=null) {
-				int ox = ic.offScreenX(e.getX());
-				int oy = ic.offScreenY(e.getX());
+				Point loc = ic.getCursorLoc();
+				int x = ic.screenX(loc.x);
+				int y = ic.screenY(loc.y);
 				if (rotation<0)
-					ic.zoomIn(ox,oy);
+					ic.zoomIn(x,y);
 				else
-					ic.zoomOut(ox,oy);
+					ic.zoomOut(x,y);
 				return;
 			}
 			if (hyperStack) {
diff --git a/ij/gui/Toolbar.java b/ij/gui/Toolbar.java
index 6613cde..2f02b14 100644
--- a/ij/gui/Toolbar.java
+++ b/ij/gui/Toolbar.java
@@ -45,7 +45,8 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 	public static final int OVAL_ROI=0, ELLIPSE_ROI=1, BRUSH_ROI=2;
 	
 	private static final String[] builtInTools = {"Arrow","Brush","Command Finder", "Developer Menu","Flood Filler",
-		"LUT Menu","Overlay Brush","Pencil","Pixel Inspector","Selection Rotator", "Spray Can","Stacks Menu"};
+		"LUT Menu","Overlay Brush","Pencil","Pixel Inspector","Selection Rotator", "Smooth Wand",
+		"Spray Can","Stacks Menu"};
 	private static final String[] builtInTools2 = {"Pixel Inspection Tool","Paintbrush Tool","Flood Fill Tool"};
 
 	private static final int NUM_TOOLS = 23;
@@ -1334,16 +1335,12 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
                 MacroInstaller mi = new MacroInstaller();
                 label = label.substring(0, label.length()-1) + ".txt";
                 path = "/macros/"+label;
-				if (IJ.shiftKeyDown()) {
-					String macros = mi.openFromIJJar(path);
-                    Editor ed = new Editor();
-                    ed.setSize(350, 300);
-                    ed.create(label, macros);
-                	IJ.setKeyUp(KeyEvent.VK_SHIFT);
-				} else {
-					resetTools();
-					mi.installFromIJJar(path);
-				}
+                if (IJ.shiftKeyDown())
+				    showCode(label, mi.openFromIJJar(path));
+				else {
+				    resetTools();
+				    mi.installFromIJJar(path);
+                }
             } else {
                 // load from ImageJ/macros/toolsets
                 if (label.equals("Startup Macros")) {
@@ -1745,38 +1742,67 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		if (label.startsWith("Arrow")) {
 			tool = new ij.plugin.tool.ArrowTool();
 			if (tool!=null) tool.run("");
+			showSource("Arrow");
 		} else if (label.startsWith("Overlay Brush")) {
 			tool = new ij.plugin.tool.OverlayBrushTool();
 			if (tool!=null) tool.run("");
+			showSource("OverlayBrush");
 		} else if (label.startsWith("Pixel Inspect")) {
 			tool = new ij.plugin.tool.PixelInspectionTool();
 			if (tool!=null) tool.run("");
+			showSource("PixelInspection");
 		} else if (label.startsWith("Brush")||label.startsWith("Paintbrush")) {
 			tool = new ij.plugin.tool.BrushTool();
 			if (tool!=null) tool.run("");
+			showSource("Brush");
 		} else if (label.startsWith("Pencil")) {
 			tool = new ij.plugin.tool.BrushTool();
 			if (tool!=null) tool.run("pencil");
+			showSource("Brush");
 		} else if (label.startsWith("Selection Rotator")) {
 			tool = new ij.plugin.tool.RoiRotationTool();
 			if (tool!=null) tool.run("");
-		} else if (label.startsWith("Flood Fill")) {
-			(new MacroInstaller()).installFromIJJar("/macros/FloodFillTool.txt");
-		} else if (label.startsWith("Spray Can")) {
-			(new MacroInstaller()).installFromIJJar("/macros/SprayCanTool.txt");
-		} else if (label.startsWith("Developer Menu")) {
-			(new MacroInstaller()).installFromIJJar("/macros/DeveloperMenuTool.txt");
-		} else if (label.startsWith("Stacks Menu")) {
-			(new MacroInstaller()).installFromIJJar("/macros/StacksMenuTool.txt");
-		} else if (label.startsWith("LUT Menu")) {
-			(new MacroInstaller()).installFromIJJar("/macros/LUTMenuTool.txt");
-		} else if (label.startsWith("Command Finder")) {
-			(new MacroInstaller()).installFromIJJar("/macros/CommandFinderTool.txt");
-		} else
+			showSource("RoiRotation");
+		} else if (label.startsWith("Flood Fill"))
+			installMacroFromJar("/macros/FloodFillTool.txt");
+		else if (label.startsWith("Spray Can"))
+			installMacroFromJar("/macros/SprayCanTool.txt");
+		else if (label.startsWith("Developer Menu"))
+			installMacroFromJar("/macros/DeveloperMenuTool.txt");
+		else if (label.startsWith("Stacks Menu"))
+			installMacroFromJar("/macros/StacksMenuTool.txt");
+		else if (label.startsWith("LUT Menu"))
+			installMacroFromJar("/macros/LUTMenuTool.txt");
+		else if (label.startsWith("Command Finder"))
+			installMacroFromJar("/macros/CommandFinderTool.txt");
+		else if (label.startsWith("Smooth Wand"))
+			installMacroFromJar("/macros/SmoothWandTool.txt");
+		else
 			ok = false;
 		return ok;
 	}
 	
+	private void showSource(String name) {
+		if (IJ.shiftKeyDown()) {
+			IJ.runPlugIn("ij.plugin.BrowserLauncher", IJ.URL+"/source/ij/plugin/tool/"+name+"Tool.java");
+			IJ.setKeyUp(KeyEvent.VK_SHIFT);
+		}
+	}
+	
+	private void installMacroFromJar(String path) {
+		if (IJ.shiftKeyDown())
+			showCode(path, (new MacroInstaller()).openFromIJJar(path));
+		else
+			(new MacroInstaller()).installFromIJJar(path);
+	}
+	
+	private void showCode(String title, String code) {
+		Editor ed = new Editor();
+		ed.setSize(550, 450);
+		ed.create(title, code);
+		IJ.setKeyUp(KeyEvent.VK_SHIFT);
+	}
+	
 	private boolean isMacroSet(int id) {
 		if (tools[id]==null)
 			return false;
diff --git a/ij/gui/Wand.java b/ij/gui/Wand.java
index 5743db0..c6c79b6 100644
--- a/ij/gui/Wand.java
+++ b/ij/gui/Wand.java
@@ -43,9 +43,9 @@ public class Wand {
     private float[] fpixels;
     private int width, height;
     private float lowerThreshold, upperThreshold;
-    private int xmin;                   //of selection created
-    private boolean exactPixelValue;    //For color, match RGB, not gray value
-    private static boolean allPoints;
+    private int xmin;                   // of selection created
+    private boolean exactPixelValue;    // For color, match RGB, not gray value
+    private static boolean allPoints;  // output contains intermediate points
 
 
     /** Constructs a Wand object from an ImageProcessor. */
@@ -260,7 +260,7 @@ public class Wand {
             }
             direction = newDirection;
         } while (x!=startX || y!=startY || (direction&3)!=startDirection);
-        if (allPoints || xpoints[0]!=x)            // if the start point = end point is a corner: add to list
+        if (xpoints[0]!=x && !allPoints)            // if the start point = end point is a corner: add to list
             addPoint(x, y);
         return (direction <= 0);        // if we have done a clockwise loop, inside pixels are enclosed
     }
@@ -340,12 +340,14 @@ public class Wand {
         return ((double)insideCount)/area<0.25;
     }
 
+    /** Set 'true' and output will contain intermediate points for straight lines longer than one pixel. */
     public static void setAllPoints(boolean b) {
         allPoints = b;
     }
 
-    public static boolean allPoints() {
-        return allPoints;
-    }
+	/** Returns 'true' if output contains intermediate points for straight lines longer than one pixel. */
+	public static boolean allPoints() {
+		return allPoints;
+	}
 
 }
diff --git a/ij/io/FileInfo.java b/ij/io/FileInfo.java
index 08c3384..d7fcfe5 100644
--- a/ij/io/FileInfo.java
+++ b/ij/io/FileInfo.java
@@ -137,8 +137,9 @@ public class FileInfo implements Cloneable {
 	public byte[][] metaData;
 	public double[] displayRanges;
 	public byte[][] channelLuts;
-	public byte[] roi;
-	public byte[][] overlay;
+	public byte[] plot;			// serialized plot
+	public byte[] roi;			// serialized roi
+	public byte[][] overlay;	// serialized overlay objects
 	public int samplesPerPixel;
 	public String openNextDir, openNextName;
     
diff --git a/ij/io/FileOpener.java b/ij/io/FileOpener.java
index fa17780..11a9b09 100644
--- a/ij/io/FileOpener.java
+++ b/ij/io/FileOpener.java
@@ -156,6 +156,10 @@ public class FileOpener {
 			imp.setProperty("Info", fi.info);
 		if (fi.sliceLabels!=null&&fi.sliceLabels.length==1&&fi.sliceLabels[0]!=null)
 			imp.setProperty("Label", fi.sliceLabels[0]);
+		if (fi.plot!=null) try {
+			Plot plot = new Plot(imp, new ByteArrayInputStream(fi.plot));
+			imp.setProperty(Plot.PROPERTY_KEY, plot);
+		} catch (Exception e) { IJ.handleException(e); }
 		if (fi.roi!=null)
 			imp.setRoi(RoiDecoder.openFromByteArray(fi.roi));
 		if (fi.overlay!=null)
diff --git a/ij/io/FileSaver.java b/ij/io/FileSaver.java
index d2f8013..1ff8b88 100644
--- a/ij/io/FileSaver.java
+++ b/ij/io/FileSaver.java
@@ -106,6 +106,10 @@ public class FileSaver {
 			fi.sliceLabels[0] = (String)label;
 		}
 		fi.description = getDescriptionString();
+		if (imp.getProperty(Plot.PROPERTY_KEY) != null) {
+			Plot plot = (Plot)(imp.getProperty(Plot.PROPERTY_KEY));
+			fi.plot = plot.toByteArray();
+		}
 		fi.roi = RoiEncoder.saveAsByteArray(imp.getRoi());
 		fi.overlay = getOverlay(imp);
 		DataOutputStream out = null;
@@ -225,6 +229,10 @@ public class FileSaver {
 		fi.description = getDescriptionString();
 		saveName = false;
 		fi.sliceLabels = imp.getStack().getSliceLabels();
+		if (imp.getProperty(Plot.PROPERTY_KEY) != null) {
+			Plot plot = (Plot)(imp.getProperty(Plot.PROPERTY_KEY));
+			fi.plot = plot.toByteArray();
+		}
 		fi.roi = RoiEncoder.saveAsByteArray(imp.getRoi());
 		fi.overlay = getOverlay(imp);
 		if (imp.isComposite()) saveDisplayRangesAndLuts(imp, fi);
@@ -289,6 +297,10 @@ public class FileSaver {
 			name = name+".tif";
 		fi.description = getDescriptionString();
 		fi.info = imp.getInfoProperty();
+		if (imp.getProperty(Plot.PROPERTY_KEY) != null) {
+			Plot plot = (Plot)(imp.getProperty(Plot.PROPERTY_KEY));
+			fi.plot = plot.toByteArray();
+		}
 		fi.roi = RoiEncoder.saveAsByteArray(imp.getRoi());
 		fi.overlay = getOverlay(imp);
 		fi.sliceLabels = imp.getStack().getSliceLabels();
diff --git a/ij/io/RoiDecoder.java b/ij/io/RoiDecoder.java
index ccccc63..c5f8a0b 100644
--- a/ij/io/RoiDecoder.java
+++ b/ij/io/RoiDecoder.java
@@ -13,7 +13,7 @@ import java.awt.geom.Rectangle2D;
 	
 	0-3		"Iout"
 	4-5		version (>=217)
-	6-7		roi type
+	6-7		roi type (encoded as one byte)
 	8-9		top
 	10-11	left
 	12-13	bottom
diff --git a/ij/io/TiffDecoder.java b/ij/io/TiffDecoder.java
index 7420c03..a547e02 100644
--- a/ij/io/TiffDecoder.java
+++ b/ij/io/TiffDecoder.java
@@ -57,9 +57,10 @@ public class TiffDecoder {
 	static final int INFO = 0x696e666f;  // "info" (Info image property)
 	static final int LABELS = 0x6c61626c;  // "labl" (slice labels)
 	static final int RANGES = 0x72616e67;  // "rang" (display ranges)
-	static final int LUTS = 0x6c757473;  // "luts" (channel LUTs)
-	static final int ROI = 0x726f6920;  // "roi " (ROI)
-	static final int OVERLAY = 0x6f766572;  // "over" (overlay)
+	static final int LUTS = 0x6c757473;    // "luts" (channel LUTs)
+	static final int PLOT = 0x706c6f74;    // "plot" (serialized plot)
+	static final int ROI = 0x726f6920;     // "roi " (ROI)
+	static final int OVERLAY = 0x6f766572; // "over" (overlay)
 	
 	private String directory;
 	private String name;
@@ -200,7 +201,7 @@ public class TiffDecoder {
 		if (id.length()<7) return;
 		fi.description = id;
         int index1 = id.indexOf("images=");
-        if (index1>0 && createdByImageJ) {
+        if (index1>0 && createdByImageJ && id.charAt(7)!='\n') {
             int index2 = id.indexOf("\n", index1);
             if (index2>0) {
                 String images = id.substring(index1+7,index2);
@@ -284,7 +285,7 @@ public class TiffDecoder {
 			
 		in.seek(offset+260);
 		int nImages = in.readShort();
-		if(nImages>=2 && (fi.fileType==FileInfo.GRAY8||fi.fileType==FileInfo.COLOR8)) {
+		if (nImages>=2 && (fi.fileType==FileInfo.GRAY8||fi.fileType==FileInfo.COLOR8)) {
 			fi.nImages = nImages;
 			fi.pixelDepth = in.readFloat();	//SliceSpacing
 			int skip = in.readShort();		//CurrentSlice
@@ -619,6 +620,7 @@ public class TiffDecoder {
 				if (types[i]==LABELS) id = " (slice labels)";
 				if (types[i]==RANGES) id = " (display ranges)";
 				if (types[i]==LUTS) id = " (luts)";
+				if (types[i]==PLOT) id = " (plot)";
 				if (types[i]==ROI) id = " (roi)";
 				if (types[i]==OVERLAY) id = " (overlay)";
 				dInfo += "   "+i+" "+Integer.toHexString(types[i])+" "+counts[i]+id+"\n";
@@ -637,6 +639,8 @@ public class TiffDecoder {
 				getDisplayRanges(start, fi);
 			else if (types[i]==LUTS)
 				getLuts(start, start+counts[i]-1, fi);
+			else if (types[i]==PLOT)
+				getPlot(start, fi);
 			else if (types[i]==ROI)
 				getRoi(start, fi);
 			else if (types[i]==OVERLAY)
@@ -722,6 +726,12 @@ public class TiffDecoder {
 		in.readFully(fi.roi, len); 
 	}
 
+	void getPlot(int first, FileInfo fi) throws IOException {
+		int len = metaDataCounts[first];
+		fi.plot = new byte[len];
+		in.readFully(fi.plot, len);
+	}
+
 	void getOverlay(int first, int last, FileInfo fi) throws IOException {
 		fi.overlay = new byte[last-first+1][];
 	    int index = 0;
diff --git a/ij/io/TiffEncoder.java b/ij/io/TiffEncoder.java
index 804b74f..3840cd0 100644
--- a/ij/io/TiffEncoder.java
+++ b/ij/io/TiffEncoder.java
@@ -196,6 +196,12 @@ public class TiffEncoder {
 			nMetaDataEntries += fi.channelLuts.length;
 		}
 
+		if (fi.plot!=null) {
+			nMetaDataEntries++;
+			size += fi.plot.length;
+			nTypes++;
+		}
+
 		if (fi.roi!=null) {
 			nMetaDataEntries++;
 			size += fi.roi.length;
@@ -373,6 +379,8 @@ public class TiffEncoder {
 			for (int i=0; i<fi.channelLuts.length; i++)
 				writeInt(out, fi.channelLuts[i].length);
 		}
+		if (fi.plot!=null)
+			writeInt(out, fi.plot.length);
 		if (fi.roi!=null)
 			writeInt(out, fi.roi.length);
 		if (fi.overlay!=null) {
@@ -400,6 +408,10 @@ public class TiffEncoder {
 			writeInt(out, TiffDecoder.LUTS); // type="luts"
 			writeInt(out, fi.channelLuts.length); // count
 		}
+		if (fi.plot!=null) {
+			writeInt(out, TiffDecoder.PLOT); // type="plot"
+			writeInt(out, 1); // count
+		}
 		if (fi.roi!=null) {
 			writeInt(out, TiffDecoder.ROI); // type="roi "
 			writeInt(out, 1); // count
@@ -428,6 +440,8 @@ public class TiffEncoder {
 			for (int i=0; i<fi.channelLuts.length; i++)
 				out.write(fi.channelLuts[i]);
 		}
+		if (fi.plot!=null)
+			out.write(fi.plot);
 		if (fi.roi!=null)
 			out.write(fi.roi);
 		if (fi.overlay!=null) {
diff --git a/ij/macro/Functions.java b/ij/macro/Functions.java
index aa7329f..f6a770c 100644
--- a/ij/macro/Functions.java
+++ b/ij/macro/Functions.java
@@ -90,7 +90,7 @@ public class Functions implements MacroConstants, Measurements {
 		this.interp = interp;
 		this.pgm = pgm;
 	}
- 
+
 	void doFunction(int type) {
 		switch (type) {
 			case RUN: doRun(); break;
@@ -111,6 +111,7 @@ public class Functions implements MacroConstants, Measurements {
 			case MAKE_ARROW: makeArrow(); break;
 			case MAKE_OVAL: makeOval(); break;
 			case MAKE_RECTANGLE: makeRectangle(); break;
+			case MAKE_ROTATED_RECT: makeRotatedRectangle(); break;
 			case DUMP: interp.dump(); break;
 			case LINE_TO: lineTo(); break;
 			case MOVE_TO: moveTo(); break;
@@ -189,12 +190,12 @@ public class Functions implements MacroConstants, Measurements {
 			case TO_UNSCALED: toUnscaled(); break;
 		}
 	}
-	
+
 	final double getFunctionValue(int type) {
 		double value = 0.0;
 		switch (type) {
 			case GET_PIXEL: value = getPixel(); break;
-			case ABS: case COS: case EXP: case FLOOR: case LOG: case ROUND: 
+			case ABS: case COS: case EXP: case FLOOR: case LOG: case ROUND:
 			case SIN: case SQRT: case TAN: case ATAN: case ASIN: case ACOS:
 				value = math(type);
 				break;
@@ -253,8 +254,8 @@ public class Functions implements MacroConstants, Measurements {
 			case GET_STRING: str = getStringDialog(); break;
 			case SUBSTRING: str = substring(); break;
 			case FROM_CHAR_CODE: str = fromCharCode(); break;
-			case GET_INFO: str = getInfo(); break;			
-			case GET_IMAGE_INFO: interp.getParens(); str = getImageInfo(); break;			
+			case GET_INFO: str = getInfo(); break;
+			case GET_IMAGE_INFO: interp.getParens(); str = getImageInfo(); break;
 			case GET_DIRECTORY: str = getDirectory(); break;
 			case GET_ARGUMENT: interp.getParens(); str=interp.argument!=null?interp.argument:""; break;
 			case TO_LOWER_CASE: str = getStringArg().toLowerCase(Locale.US); break;
@@ -284,7 +285,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return str;
 	}
-	
+
 	private void setLineWidth(int width) {
 		lineWidth = width;
 		getProcessor().setLineWidth(width);
@@ -449,7 +450,7 @@ public class Functions implements MacroConstants, Measurements {
 		if (array!=null) {
 			int index = interp.getIndex();
 			checkIndex(index, 0, v.getArraySize()-1);
-			v = array[index]; 
+			v = array[index];
 		}
 		return v;
 	}
@@ -544,7 +545,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Array expected");
 		return a;
 	}
-		
+
 	private Color getColor() {
 		String color = getString();
 		color = color.toLowerCase(Locale.US);
@@ -574,8 +575,8 @@ public class Functions implements MacroConstants, Measurements {
 			return Color.yellow;
 		else if (color.equals("pink"))
 			return Color.pink;
-		else if (color.startsWith("#")) 
-			return Colors.decode(color, Color.black); 
+		else if (color.startsWith("#"))
+			return Colors.decode(color, Color.black);
 		else
 			interp.error("'red', 'green', or '#0000ff' etc. expected");
 		return null;
@@ -645,7 +646,7 @@ public class Functions implements MacroConstants, Measurements {
 			blue = (int)getLastArg();
 		}
 		IJ.setBackgroundColor(red, green, blue);
-		resetImage(); 
+		resetImage();
 	}
 
 	void setColor() {
@@ -662,13 +663,13 @@ public class Functions implements MacroConstants, Measurements {
 		if (interp.nextToken()==')')
 			{interp.getRightParen(); setColor(arg1); return;}
 		int red=(int)arg1, green=(int)getNextArg(), blue=(int)getLastArg();
-		if (red<0) red=0; if (green<0) green=0; if (blue<0) blue=0; 
-		if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;  
+		if (red<0) red=0; if (green<0) green=0; if (blue<0) blue=0;
+		if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;
 		defaultColor = new Color(red, green, blue);
 		getProcessor().setColor(defaultColor);
 		defaultValue = Double.NaN;
 	}
-	
+
 	void setColor(double value) {
 		ImageProcessor ip = getProcessor();
 		ImagePlus imp = getImage();
@@ -730,7 +731,7 @@ public class Functions implements MacroConstants, Measurements {
 				interp.error("More than "+max+" points");
 			getImage().setRoi(new PolygonRoi(x, y, n, Roi.POLYLINE));
 		}
-		resetImage(); 
+		resetImage();
 	}
 
 	void makeArrow() {
@@ -757,7 +758,7 @@ public class Functions implements MacroConstants, Measurements {
 		resetImage();
 		shiftKeyDown = altKeyDown = false;
 	}
-	
+
 	void makeRectangle() {
 		Roi previousRoi = getImage().getRoi();
 		if (shiftKeyDown||altKeyDown) getImage().saveRoi();
@@ -784,7 +785,12 @@ public class Functions implements MacroConstants, Measurements {
 		shiftKeyDown = altKeyDown = false;
 		IJ.setKeyUp(IJ.ALL_KEYS);
 	}
-	
+
+	void makeRotatedRectangle() {
+		getImage().setRoi(new RotatedRectRoi(getFirstArg(), getNextArg(), getNextArg(), getNextArg(), getLastArg()));
+		resetImage();
+	}
+
 	ImagePlus getImage() {
 		ImagePlus imp = IJ.getImage();
 		if (imp.getWindow()==null && IJ.getInstance()!=null && !interp.isBatchMode() && WindowManager.getTempCurrentImage()==null)
@@ -793,7 +799,7 @@ public class Functions implements MacroConstants, Measurements {
 		defaultImp = imp;
 		return imp;
 	}
-	
+
 	void resetImage() {
 		defaultImp = null;
 		defaultIP = null;
@@ -870,7 +876,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return value;
 	}
-	
+
 	void setZCoordinate() {
 		int z = (int)getArg();
 		int n = z + 1;
@@ -879,9 +885,9 @@ public class Functions implements MacroConstants, Measurements {
 		int size = stack.getSize();
 		if (z<0 || z>=size)
 			interp.error("Z coordinate ("+z+") is out of 0-"+(size-1)+ " range");
-		this.defaultIP = stack.getProcessor(n);		
+		this.defaultIP = stack.getProcessor(n);
 	}
-	
+
 	void moveTo() {
 		interp.getLeftParen();
 		int a1 = (int)Math.round(interp.getExpression());
@@ -890,7 +896,7 @@ public class Functions implements MacroConstants, Measurements {
 		interp.getRightParen();
 		getProcessor().moveTo(a1, a2);
 	}
-	
+
 	void lineTo() {
 		interp.getLeftParen();
 		int a1 = (int)Math.round(interp.getExpression());
@@ -918,7 +924,7 @@ public class Functions implements MacroConstants, Measurements {
 		ip.drawLine(x1, y1, x2, y2);
 		updateAndDraw();
 	}
-	
+
 	void setForegroundColor(ImageProcessor ip) {
 		if (defaultColor!=null)
 			ip.setColor(defaultColor);
@@ -930,7 +936,7 @@ public class Functions implements MacroConstants, Measurements {
 	}
 
 	void doIPMethod(int type) {
-		interp.getParens(); 
+		interp.getParens();
 		ImageProcessor ip = getProcessor();
 		switch (type) {
 			case SNAPSHOT: ip.snapshot(); break;
@@ -938,7 +944,7 @@ public class Functions implements MacroConstants, Measurements {
 				ip.reset();
 				updateNeeded = true;
 				break;
-			case FILL: 
+			case FILL:
 				ImagePlus imp = getImage();
 				Roi roi = imp.getRoi();
 				if (!colorSet) setForegroundColor(ip);
@@ -964,7 +970,7 @@ public class Functions implements MacroConstants, Measurements {
 		} else
 			updateNeeded = true;
 	}
-	
+
 	void updateDisplay() {
 		if (updateNeeded && WindowManager.getImageCount()>0) {
 			ImagePlus imp = getImage();
@@ -998,7 +1004,7 @@ public class Functions implements MacroConstants, Measurements {
 			ip.drawString(str, x, y);
 		updateAndDraw();
 	}
-	
+
 	void setFont(ImageProcessor ip) {
 		if (font!=null && !fontSet)
 			ip.setFont(font);
@@ -1065,7 +1071,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.done = true;
 	}
 
-	Random ran;	
+	Random ran;
 	double random() {
 		double dseed = Double.NaN;
 		boolean gaussian = false;
@@ -1096,7 +1102,7 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			return ran.nextDouble();
 	}
-	
+
 	//void setSeed() {
 	//	long seed = (long)getArg();
 	//	if (ran==null)
@@ -1201,7 +1207,7 @@ public class Functions implements MacroConstants, Measurements {
 		if (isStringArg() || isLabel)
 			stringValue = getString();
 		else
-			value = interp.getExpression();		
+			value = interp.getExpression();
 		interp.getRightParen();
 		ResultsTable rt = Analyzer.getResultsTable();
 		if (row<0 || row>rt.getCounter())
@@ -1221,7 +1227,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error(""+e.getMessage());
 		}
 	}
-	
+
 	void updateResults() {
 		interp.getParens();
 		ResultsTable rt = Analyzer.getResultsTable();
@@ -1272,14 +1278,14 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			return 0.0;
 	}
-	
+
 	String getStringDialog() {
 		interp.getLeftParen();
 		String prompt = getString();
 		interp.getComma();
 		String defaultStr = getString();
 		interp.getRightParen();
-		
+
 		String title = interp.macroName!=null?interp.macroName:"";
 		if (title.endsWith(" Options"))
 			title = title.substring(0, title.length()-8);
@@ -1305,17 +1311,17 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			return Integer.toHexString(arg);
 	}
-	
+
 	double getStackSize() {
 		interp.getParens();
 		return getImage().getStackSize();
 	}
-	
+
 	double getImageCount() {
 		interp.getParens();
 		return WindowManager.getImageCount();
 	}
-	
+
 	double getResultsCount() {
 		interp.getParens();
 		return Analyzer.getResultsTable().getCounter();
@@ -1359,7 +1365,7 @@ public class Functions implements MacroConstants, Measurements {
 		xCoordinates.setArray(xa);
 		yCoordinates.setArray(ya);
 	}
-	
+
 	Variable[] getProfile() {
 		interp.getParens();
 		ImagePlus imp = getImage();
@@ -1392,7 +1398,7 @@ public class Functions implements MacroConstants, Measurements {
     		array[i] = new Variable();
     	return array;
 	}
-	
+
 	Variable[] split() {
 		String s1 = getFirstString();
 		String s2 = null;
@@ -1408,14 +1414,14 @@ public class Functions implements MacroConstants, Measurements {
 		else if (s1.length()>0 && s2!=null && s2.length()>=3 && s2.startsWith("(")&&s2.endsWith(")")) {
 			s2 = s2.substring(1,s2.length()-1);
 			strings = s1.split(s2,-1);
-		} else 
+		} else
 			strings = (s2==null||s2.equals(""))?Tools.split(s1):Tools.split(s1, s2);
     	Variable[] array = new Variable[strings.length];
     	for (int i=0; i<strings.length; i++)
     		array[i] = new Variable(0, 0.0, strings[i]);
     	return array;
 	}
-	
+
 	Variable[] getFileList() {
 		String dir = getStringArg();
 		File f = new File(dir);
@@ -1455,7 +1461,7 @@ public class Functions implements MacroConstants, Measurements {
     		array[i] = new Variable(0, 0.0, list[i]);
     	return array;
 	}
-	
+
 	Variable[] initNewArray() {
 		Vector vector = new Vector();
 		int size = 0;
@@ -1467,7 +1473,7 @@ public class Functions implements MacroConstants, Measurements {
 				v.setValue(interp.getExpression());
 			vector.addElement(v);
 			size++;
-			interp.getToken();				
+			interp.getToken();
 		} while (interp.token==',');
 		if (interp.token!=')')
 			interp.error("';' expected");
@@ -1496,7 +1502,7 @@ public class Functions implements MacroConstants, Measurements {
 			if (interp.nextToken()==',')
 				interp.getToken();
 		}
-		interp.getRightParen();		
+		interp.getRightParen();
 		return new String(chars, 0, count);
 	}
 
@@ -1509,7 +1515,7 @@ public class Functions implements MacroConstants, Measurements {
 			return getWindowContents();
 		}
 	}
-	
+
 	String getInfo(String key) {
 			String lowercaseKey = key.toLowerCase(Locale.US);
 			int len = lowercaseKey.length();
@@ -1600,7 +1606,7 @@ public class Functions implements MacroConstants, Measurements {
 			}
 			return "";
 	}
-	
+
 	private String getWindowTitle() {
 		Window win = WindowManager.getActiveWindow();
 		if (IJ.debugMode) IJ.log("getWindowTitle: "+win);
@@ -1641,21 +1647,21 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return type;
 	}
-		
+
 	String getWindowContents() {
 		Frame frame = WindowManager.getFrontWindow();
 		if (frame!=null && frame instanceof TextWindow) {
 			TextPanel tp = ((TextWindow)frame).getTextPanel();
-			return tp.getText();			
+			return tp.getText();
 		} else if (frame!=null && frame instanceof Editor) {
-			return ((Editor)frame).getText();			
+			return ((Editor)frame).getText();
 		} else if (frame!=null && frame instanceof Recorder) {
-			return ((Recorder)frame).getText();			
+			return ((Recorder)frame).getText();
 		} else
 			return getImageInfo();
 	}
-	
-	String getImageInfo() {		
+
+	String getImageInfo() {
 		ImagePlus imp = getImage();
 		ImageInfo infoPlugin = new ImageInfo();
 		return infoPlugin.getImageInfo(imp);
@@ -1694,7 +1700,7 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			IJ.showMessage(title, message);
 	}
-	
+
 	double lengthOf() {
 		int length = 0;
 		interp.getLeftParen();
@@ -1703,7 +1709,7 @@ public class Functions implements MacroConstants, Measurements {
 			case STRING_FUNCTION:
 			case USER_FUNCTION:
 				length = getString().length();
-				break; 
+				break;
 			case WORD:
 				if (pgm.code[interp.pc+2]=='[') {
 					length = getString().length();
@@ -1721,7 +1727,7 @@ public class Functions implements MacroConstants, Measurements {
 						length = v.getArraySize();
 					else
 						interp.error("String or array expected");
-				}					
+				}
 				break;
 			default:
 				interp.error("String or array expected");
@@ -1729,7 +1735,7 @@ public class Functions implements MacroConstants, Measurements {
 		interp.getRightParen();
 		return length;
 	}
-	
+
 	void getCursorLoc() {
 		Variable x = getFirstVariable();
 		Variable y = getNextVariable();
@@ -1745,7 +1751,7 @@ public class Functions implements MacroConstants, Measurements {
 		Roi roi = imp.getRoi();
 		flags.setValue(ic.getModifiers()+((roi!=null)&&roi.contains(p.x,p.y)?32:0));
 	}
-	
+
 	void getLine() {
 		Variable vx1 = getFirstVariable();
 		Variable vy1 = getNextVariable();
@@ -1762,10 +1768,10 @@ public class Functions implements MacroConstants, Measurements {
 		vx1.setValue(x1);
 		vy1.setValue(y1);
 		vx2.setValue(x2);
-		vy2.setValue(y2);				
-		lineWidth.setValue(roi!=null?roi.getStrokeWidth():1);				
+		vy2.setValue(y2);
+		lineWidth.setValue(roi!=null?roi.getStrokeWidth():1);
 	}
-	
+
 	void getVoxelSize() {
 		Variable width = getFirstVariable();
 		Variable height = getNextVariable();
@@ -1778,7 +1784,7 @@ public class Functions implements MacroConstants, Measurements {
 		depth.setValue(cal.pixelDepth);
 		unit.setString(cal.getUnits());
 	}
-	
+
 	void getHistogram() {
 		interp.getLeftParen();
 		Variable values = null;
@@ -1799,7 +1805,7 @@ public class Functions implements MacroConstants, Measurements {
 			if (bitDepth==8 || bitDepth==24)
 				interp.error("16 or 32-bit image required to set histMin and histMax");
 			setMinMax = true;
-		} else 
+		} else
 			interp.getRightParen();
 		if (nBins==65536 && bitDepth==16) {
 			Variable[] array = counts.getArray();
@@ -1847,7 +1853,7 @@ public class Functions implements MacroConstants, Measurements {
 		} else
 			counts.setArray(new Variable(stats.histogram).getArray());
 	}
-	
+
 	void getLut() {
 		Variable reds = getFirstArrayVariable();
 		Variable greens = getNextArrayVariable();
@@ -1866,8 +1872,8 @@ public class Functions implements MacroConstants, Measurements {
 		byte[] rLUT = new byte[mapSize];
 		byte[] gLUT = new byte[mapSize];
 		byte[] bLUT = new byte[mapSize];
-		cm.getReds(rLUT); 
-		cm.getGreens(gLUT); 
+		cm.getReds(rLUT);
+		cm.getGreens(gLUT);
 		cm.getBlues(bLUT);
 		reds.setArray(new Variable(rLUT).getArray());
 		greens.setArray(new Variable(gLUT).getArray());
@@ -1878,7 +1884,7 @@ public class Functions implements MacroConstants, Measurements {
 		double[] reds = getFirstArray();
 		double[] greens = getNextArray();
 		double[] blues = getLastArray();
-		int length = reds.length;		
+		int length = reds.length;
 		if (greens.length!=length || blues.length!=length)
 			interp.error("Arrays are not the same length");
 		ImagePlus imp = getImage();
@@ -1914,8 +1920,8 @@ public class Functions implements MacroConstants, Measurements {
 			t2 = -1;
 		} else {
 			Calibration cal = imp.getCalibration();
-			t1 = cal.getCValue(t1); 
-			t2 = cal.getCValue(t2); 
+			t1 = cal.getCValue(t1);
+			t2 = cal.getCValue(t2);
 		}
 		lower.setValue(t1);
 		upper.setValue(t2);
@@ -1962,11 +1968,11 @@ public class Functions implements MacroConstants, Measurements {
 			roiType = (int)interp.getExpression();
 			if (roiType<0 || roiType==Roi.COMPOSITE)
 				interp.error("Invalid selection type ("+roiType+")");
-			if (roiType==Roi.RECTANGLE) roiType = Roi.POLYGON;		
-			if (roiType==Roi.OVAL) roiType = Roi.FREEROI;		
+			if (roiType==Roi.RECTANGLE) roiType = Roi.POLYGON;
+			if (roiType==Roi.OVAL) roiType = Roi.FREEROI;
 		}
 		double[] x = getNextArray();
-		int n = x.length;		
+		int n = x.length;
 		interp.getComma();
 		double[] y = getNumericArray();
 		if (interp.nextToken()==',') {
@@ -1974,7 +1980,7 @@ public class Functions implements MacroConstants, Measurements {
 			if (n>x.length || n>y.length)
 				interp.error("Array too short");
 		} else {
-			interp.getRightParen();			
+			interp.getRightParen();
 			if (y.length!=n)
 				interp.error("Arrays are not the same length");
 		}
@@ -2030,7 +2036,7 @@ public class Functions implements MacroConstants, Measurements {
 		if (roiType==Roi.POLYGON || roiType==Roi.FREEROI) {
 			roi = imp.getRoi();
 			if (previousRoi!=null && roi!=null)
-				updateRoi(roi); 
+				updateRoi(roi);
 		}
 		updateNeeded = false;
 		shiftKeyDown = altKeyDown = false;
@@ -2072,7 +2078,7 @@ public class Functions implements MacroConstants, Measurements {
 			return;
 		} else if (name.equals("setLogScaleX")) {
 			if (interp.nextNextToken()==')') {  //no-argument call setLogScaleX() means true
-				interp.getParens();			
+				interp.getParens();
 				currentPlot.setAxisXLog(true);
 			} else
 				currentPlot.setAxisXLog(getBooleanArg());
@@ -2080,7 +2086,7 @@ public class Functions implements MacroConstants, Measurements {
 			return;
 		} else if (name.equals("setLogScaleY")) {
 			if (interp.nextNextToken()==')') {
-				interp.getParens();			
+				interp.getParens();
 				currentPlot.setAxisYLog(true);
 			} else
 				currentPlot.setAxisYLog(getBooleanArg());
@@ -2091,7 +2097,7 @@ public class Functions implements MacroConstants, Measurements {
 			return;
 		} else if (name.equals("freeze")) {
 			if (interp.nextNextToken()==')') {
-				interp.getParens();			
+				interp.getParens();
 				currentPlot.setFrozen(true);
 			} else
 				currentPlot.setFrozen(getBooleanArg());
@@ -2124,6 +2130,9 @@ public class Functions implements MacroConstants, Measurements {
 		} else if (name.equals("useTemplate")) {
 			usePlotTemplate(currentPlot);
 			return;
+		} else if (name.equals("getFrameBounds")) {
+			getPlotFrameBounds(currentPlot);
+			return;
 		}
 		// the following commands need a plot under construction
 		if (plot==null)
@@ -2132,22 +2141,22 @@ public class Functions implements MacroConstants, Measurements {
 			showPlot();
 			return;
 		} else if (name.equals("update")) {
-			updatePlot(); 
+			updatePlot();
 			return;
 		} else if (name.equals("addText") || name.equals("drawLabel")) {
-			addPlotText(); 
+			addPlotText();
 			return;
 		} else if (name.equals("drawLine")) {
-			drawPlotLine(false); 
+			drawPlotLine(false);
 			return;
 		} else if (name.equals("drawNormalizedLine")) {
-			drawPlotLine(true); 
+			drawPlotLine(true);
 			return;
 		} else if (name.equals("drawVectors")) {
-			drawPlotVectors(); 
+			drawPlotVectors();
 			return;
 		} else if (name.startsWith("setLineWidth")) {
-			plot.setLineWidth((float)getArg()); 
+			plot.setLineWidth((float)getArg());
 			return;
 		} else if (name.startsWith("setJustification")) {
 			setJustification();
@@ -2155,7 +2164,7 @@ public class Functions implements MacroConstants, Measurements {
 		} else if (name.equals("add")) {
 			String arg = getFirstString();
 			int what = Plot.toShape(arg);
-			addToPlot(what); 
+			addToPlot(what);
 			return;
 		} else
 			interp.error("Unrecognized plot function");
@@ -2228,9 +2237,9 @@ public class Functions implements MacroConstants, Measurements {
 				y = getNextArray();
 		}
 		interp.getRightParen();
-		plot = new Plot(title, xLabel, yLabel, x, y);							
+		plot = new Plot(title, xLabel, yLabel, x, y);
 	}
-	
+
 	void showPlot() {
 		if (plot!=null) {
 			PlotWindow plotWindow = plot.show();
@@ -2238,7 +2247,7 @@ public class Functions implements MacroConstants, Measurements {
 				plotID = plotWindow.getImagePlus().getID();
 		}
 		plot = null;
-		interp.getParens();			
+		interp.getParens();
 	}
 
 	void updatePlot() {
@@ -2254,9 +2263,9 @@ public class Functions implements MacroConstants, Measurements {
 			}
 		}
 		plot = null;
-		interp.getParens();			
+		interp.getParens();
 	}
-	
+
 	void addPlotText() {
 		String str = getFirstString();
 		double x = getNextArg();
@@ -2365,15 +2374,19 @@ public class Functions implements MacroConstants, Measurements {
 	}
 
 	void getPlotLimits(Plot plot) {
-		Variable xMin = getFirstVariable();
-		Variable xMax = getNextVariable();
-		Variable yMin = getNextVariable();
-		Variable yMax = getLastVariable();
 		double[] limits = plot.getLimits();
-		xMin.setValue(limits[0]);
-		xMax.setValue(limits[1]);
-		yMin.setValue(limits[2]);
-		yMax.setValue(limits[3]);
+		getFirstVariable().setValue(limits[0]);  //xMin
+		getNextVariable().setValue(limits[1]);   //xMax
+		getNextVariable().setValue(limits[2]);   //yMin
+		getLastVariable().setValue(limits[3]);   //yMax
+	}
+
+	void getPlotFrameBounds(Plot plot) {
+		Rectangle r = plot.getDrawingFrame();
+		getFirstVariable().setValue(r.x);
+		getNextVariable().setValue(r.y);
+		getNextVariable().setValue(r.width);
+		getLastVariable().setValue(r.height);
 	}
 
 	void makeHighResolution(Plot plot) {
@@ -2418,7 +2431,7 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			plot.addPoints(x, y, what);
 	}
-	
+
 	void getBounds() {
 		Variable x = getFirstVariable();
 		Variable y = getNextVariable();
@@ -2447,7 +2460,7 @@ public class Functions implements MacroConstants, Measurements {
 		if (interp.nextToken()==',')
 			index2 = (int)getLastArg();
 		else
-			interp.getRightParen();			
+			interp.getRightParen();
 		if (index1>index2)
 			interp.error("beginIndex>endIndex");
 		checkIndex(index1, 0, s.length());
@@ -2463,7 +2476,7 @@ public class Functions implements MacroConstants, Measurements {
 			fromIndex = (int)getLastArg();
 			checkIndex(fromIndex, 0, s1.length()-1);
 		} else
-			interp.getRightParen();			
+			interp.getRightParen();
 		if (fromIndex==0)
 			return s1.indexOf(s2);
 		else
@@ -2487,7 +2500,7 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			return 1.0; //true
 	}
-	
+
 	double isOpen() {
 		interp.getLeftParen();
 		if (isStringArg()) {
@@ -2500,7 +2513,7 @@ public class Functions implements MacroConstants, Measurements {
 			return WindowManager.getImage(id)==null?0.0:1.0;
 		}
 	}
-	
+
 	boolean isOpen(String title) {
 		boolean open = WindowManager.getWindow(title)!=null;
 		if (open)
@@ -2543,7 +2556,7 @@ public class Functions implements MacroConstants, Measurements {
 			IJ.showMessage("Macro", msg);
 		throw new RuntimeException(Macro.MACRO_CANCELED);
 	}
-	
+
 	void showProgress() {
 		ImageJ ij = IJ.getInstance();
 		ij.gui.ProgressBar progressBar = ij!=null?ij.getProgressBar():null;
@@ -2552,13 +2565,13 @@ public class Functions implements MacroConstants, Measurements {
 		if (interp.nextToken()==',') {
 			interp.getComma();
 			double arg2 = interp.getExpression();
-			if (progressBar!=null) progressBar.show((arg1+1.0)/arg2, true);						
-		} else 
+			if (progressBar!=null) progressBar.show((arg1+1.0)/arg2, true);
+		} else
 			if (progressBar!=null) progressBar.show(arg1, true);
 		interp.getRightParen();
-		interp.showingProgress = true; 	
+		interp.showingProgress = true;
 	}
-	
+
 	void saveSettings() {
 		interp.getParens();
 		usePointerCursor = Prefs.usePointerCursor;
@@ -2595,7 +2608,7 @@ public class Functions implements MacroConstants, Measurements {
 		profileVerticalProfile = Prefs.verticalProfile;
 		profileSubPixelResolution = Prefs.subPixelResolution;
 	}
-	
+
 	void restoreSettings() {
 		interp.getParens();
 		if (!saveSettingsCalled)
@@ -2634,7 +2647,7 @@ public class Functions implements MacroConstants, Measurements {
 		Prefs.verticalProfile = profileVerticalProfile;
 		Prefs.subPixelResolution = profileSubPixelResolution;
 	}
-	
+
 	void setKeyDown() {
 		String keys = getStringArg();
 		keys = keys.toLowerCase(Locale.US);
@@ -2647,7 +2660,7 @@ public class Functions implements MacroConstants, Measurements {
 		if (shiftKeyDown)
 			IJ.setKeyDown(KeyEvent.VK_SHIFT);
 		else
-			IJ.setKeyUp(KeyEvent.VK_SHIFT);		
+			IJ.setKeyUp(KeyEvent.VK_SHIFT);
 		if (keys.equals("space"))
 			IJ.setKeyDown(KeyEvent.VK_SPACE);
 		else
@@ -2657,7 +2670,7 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			interp.keysSet = true;
 	}
-	
+
 	void abortPluginOrMacro() {
 		Interpreter.abortPrevious();
 		IJ.setKeyDown(KeyEvent.VK_ESCAPE);
@@ -2669,9 +2682,8 @@ public class Functions implements MacroConstants, Measurements {
 				win.running2 = false;
 			}
 		}
-		//Macro.abort();
 	}
-	
+
 	void open() {
 		File f = null;
 		interp.getLeftParen();
@@ -2716,7 +2728,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		resetImage();
 	}
-	
+
 	double roiManager() {
 		String cmd = getFirstString();
 		cmd = cmd.toLowerCase();
@@ -2791,9 +2803,9 @@ public class Functions implements MacroConstants, Measurements {
 			if (!rm.runCommand(cmd))
 				interp.error("Invalid ROI Manager command");
 		}
-		return countOrIndex;			
+		return countOrIndex;
 	}
-	
+
 	boolean isArrayArg() {
 		int nextToken = pgm.code[interp.pc+1];
 		int tok = nextToken&0xff;
@@ -2820,7 +2832,7 @@ public class Functions implements MacroConstants, Measurements {
 		rm.setSelectedIndexes(selectedIndexes);
 		return Double.NaN;
 	}
-	
+
 	void setFont() {
 		String name = getFirstString();
 		int size = 0;
@@ -2853,12 +2865,12 @@ public class Functions implements MacroConstants, Measurements {
 		double v1 = imp.getDisplayRangeMin();
 		double v2 = imp.getDisplayRangeMax();
 		Calibration cal = imp.getCalibration();
-		v1 = cal.getCValue(v1); 
-		v2 = cal.getCValue(v2); 
+		v1 = cal.getCValue(v1);
+		v2 = cal.getCValue(v2);
 		min.setValue(v1);
 		max.setValue(v2);
 	}
-	
+
 	void selectImage() {
 		interp.getLeftParen();
 		if (isStringArg()) {
@@ -2877,7 +2889,7 @@ public class Functions implements MacroConstants, Measurements {
 		resetImage();
 		interp.selectCount++;
 	}
-	
+
 	void selectImage(String title) {
 		if (Interpreter.isBatchMode()) {
 			if (Interpreter.imageTable!=null) {
@@ -2897,7 +2909,7 @@ public class Functions implements MacroConstants, Measurements {
 		} else
 			selectWindowManagerImage(title);
 	}
-	
+
 	void notFound(String title) {
 		interp.error(title+" not found");
 	}
@@ -3041,21 +3053,21 @@ public class Functions implements MacroConstants, Measurements {
 			displayBatchModeImage(cImp);
 		}
 	}
-	
+
 	void displayBatchModeImage(ImagePlus imp2) {
 		if (imp2!=null) {
 			ImageWindow win = imp2.getWindow();
 			if (win==null)
 				imp2.show();
 			else {
-				if (!win.isVisible()) win.show(); 
+				if (!win.isVisible()) win.show();
 				imp2.updateAndDraw();
 			}
 			Roi roi = imp2.getRoi();
 			if (roi!=null) imp2.setRoi(roi);
 		}
 	}
-	
+
 	void setLocation() {
 		int x = (int)getFirstArg();
 		int y = (int)getNextArg();
@@ -3076,7 +3088,7 @@ public class Functions implements MacroConstants, Measurements {
 				win.setLocationAndSize(x, y, width, height);
 		}
 	}
-	
+
 	void setSlice() {
 		int n = (int)getArg();
 		ImagePlus imp = getImage();
@@ -3093,7 +3105,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		resetImage();
 	}
-	
+
 	void newImage() {
 		String title = getFirstString();
 		String type = getNextString();
@@ -3144,7 +3156,7 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			return ic.getMagnification();
 	}
-	
+
 	void setAutoThreshold() {
 		String mString = null;
 		if (interp.nextToken()=='(') {
@@ -3172,14 +3184,14 @@ public class Functions implements MacroConstants, Measurements {
 		img.updateAndDraw();
 		resetImage();
 	}
-	
+
 	double parseDouble(String s) {
 			if (s==null) return 0.0;
 			s = s.trim();
 			if (s.indexOf(' ')!=-1) s = s.substring(0, s.indexOf(' '));
 			return Tools.parseDouble(s);
 	}
-	
+
 	double parseInt() {
 		String s = getFirstString();
 		int radix = 10;
@@ -3199,7 +3211,7 @@ public class Functions implements MacroConstants, Measurements {
 		} catch (NumberFormatException e) {
 			n = Double.NaN;
 		}
-		return n;			
+		return n;
 	}
 
 	void print() {
@@ -3231,7 +3243,7 @@ public class Functions implements MacroConstants, Measurements {
 		IJ.log(s);
 		interp.inPrint = false;
 	}
-	
+
 	void printToWindow(String s) {
 		String title = s.substring(1, s.length()-1);
 		String s2 = getLastString();
@@ -3265,7 +3277,7 @@ public class Functions implements MacroConstants, Measurements {
 			}
 		}
 	}
-	
+
 	void handleEditorCommand(Editor ed, String s) {
 		if (s.startsWith("\\Update:")) {
 			TextArea ta = ed.getTextArea();
@@ -3310,7 +3322,7 @@ public class Functions implements MacroConstants, Measurements {
 			tp.append(s);
 	}
 
-	
+
 	double isKeyDown() {
 		double value = 0.0;
 		String key = getStringArg().toLowerCase(Locale.US);
@@ -3320,7 +3332,7 @@ public class Functions implements MacroConstants, Measurements {
 		else interp.error("Invalid key");
 		return value;
 	}
-	
+
 	String runMacro(boolean eval) {
 		interp.getLeftParen();
 		String name = getString();
@@ -3451,7 +3463,7 @@ public class Functions implements MacroConstants, Measurements {
 			hist.setArray(array);
 		}
 	}
-	
+
 	String replace() {
 		String s1 = getFirstString();
 		String s2 = getNextString();
@@ -3467,7 +3479,7 @@ public class Functions implements MacroConstants, Measurements {
 			}
 		}
 	}
-	
+
 	void floodFill() {
 		int x = (int)getFirstArg();
 		int y = (int)getNextArg();
@@ -3489,13 +3501,13 @@ public class Functions implements MacroConstants, Measurements {
 		if (Recorder.record && pgm.hasVars)
 			Recorder.record("floodFill", x, y);
 	}
-	
+
 	void restorePreviousTool() {
 		interp.getParens();
 		Toolbar tb = Toolbar.getInstance();
 		if (tb!=null) tb.restorePreviousTool();
 	}
-	
+
 	void setVoxelSize() {
 		double width = getFirstArg();
 		double height = getNextArg();
@@ -3528,7 +3540,7 @@ public class Functions implements MacroConstants, Measurements {
 		v3.setValue(w);
 		v4.setValue(h);
 	}
-		
+
 	String doDialog() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -3543,7 +3555,7 @@ public class Functions implements MacroConstants, Measurements {
 				return null;
 			}
 			if (gd==null) {
-				interp.error("No dialog created with Dialog.create()"); 
+				interp.error("No dialog created with Dialog.create()");
 				return null;
 			}
 			if (name.equals("addString")) {
@@ -3628,7 +3640,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return null;
 	}
-	
+
 	void addCheckboxGroup(GenericDialog gd) {
 		int rows = (int)getFirstArg();
 		int columns = (int)getNextArg();
@@ -3673,7 +3685,7 @@ public class Functions implements MacroConstants, Measurements {
 		second.setValue(date.get(Calendar.SECOND));
 		millisecond.setValue(date.get(Calendar.MILLISECOND));
 	}
-	
+
 	void setMetadata() {
 		String metadata = null;
 		String arg1 = getFirstString();
@@ -3727,7 +3739,7 @@ public class Functions implements MacroConstants, Measurements {
 				metadata = (String)imp.getProperty("Label");
 				if (metadata==null && noArg)
 					metadata = (String)imp.getProperty("Info");
-			} else 
+			} else
 				metadata = imp.getStack().getSliceLabel(imp.getCurrentSlice());
 		} else {
 			metadata = (String)imp.getProperty("Info");
@@ -3798,17 +3810,17 @@ public class Functions implements MacroConstants, Measurements {
 		imp.setRoi(new PolygonRoi(x, y, n, Roi.POLYGON));
 		Roi roi = imp.getRoi();
 		if (previousRoi!=null && roi!=null)
-			updateRoi(roi); 
-		resetImage(); 
+			updateRoi(roi);
+		resetImage();
 		shiftKeyDown = altKeyDown = false;
 	}
-	
+
 	void updateRoi(Roi roi) {
 		if (shiftKeyDown || altKeyDown)
 			roi.update(shiftKeyDown, altKeyDown);
 		shiftKeyDown = altKeyDown = false;
 	}
-	
+
 	String doFile() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -3846,7 +3858,7 @@ public class Functions implements MacroConstants, Measurements {
 		} else if (name.equals("rename")) {
 			File f1 = new File(getFirstString());
 			File f2 = new File(getLastString());
-			if (checkPath(f1) && checkPath(f2)) 
+			if (checkPath(f1) && checkPath(f2))
 				return f1.renameTo(f2)?"1":"0";
 			else
 				return "0";
@@ -3891,7 +3903,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Unrecognized File function "+name);
 		return null;
 	}
-	
+
 	String nameWithoutExtension() {
 		String name = OpenDialog.getLastName();
 		if (name==null) return "";
@@ -3900,7 +3912,7 @@ public class Functions implements MacroConstants, Measurements {
 			name = name.substring(0, dotIndex);
 		return name;
 	}
-		
+
 	boolean checkPath(File f) {
 		String path = f.getPath();
 		if (path.equals("0") || path.equals("NaN")) {
@@ -3938,7 +3950,7 @@ public class Functions implements MacroConstants, Measurements {
 			name = roi.getName();
 		return name!=null?name:"";
 	}
-	
+
 	String openFile() {
 		if (writer!=null) {
 			interp.error("Currently, only one file can be open at a time");
@@ -3991,7 +4003,7 @@ public class Functions implements MacroConstants, Measurements {
 		if (interp.nextToken()==',') {
 			max = (int)getNextArg();
 			specifiedMax = true;
-		} 
+		}
 		interp.getRightParen();
 		if (path.equals("")) {
 			OpenDialog od = new OpenDialog("Open As String", "");
@@ -4024,7 +4036,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return str;
 	}
-	
+
 	String closeFile() {
 		String f = getStringArg();
 		if (!f.equals("~0~"))
@@ -4035,7 +4047,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return null;
 	}
-	
+
 	// Based on the method with the same name in Tobias Pietzsch's TifBenchmark class. */
 	public static String copyFile(File source, File destination) {
 		try {
@@ -4143,10 +4155,10 @@ public class Functions implements MacroConstants, Measurements {
 		} catch(Exception e) {
 			IJ.log("Call error ("+e+")");
 			return null;
-		} 
-			
+		}
+
  	}
- 	
+
  	Variable[] getFontList() {
 		interp.getParens();
 		String fonts[] = null;
@@ -4158,7 +4170,7 @@ public class Functions implements MacroConstants, Measurements {
     		array[i] = new Variable(0, 0.0, fonts[i]);
     	return array;
 	}
-		
+
 	void setOption() {
 		String arg1 = getFirstString();
 		boolean state = true;
@@ -4232,10 +4244,10 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			interp.error("Invalid option");
 	}
-	
+
 	void setMeasurementOption(String option) {
 	}
-	
+
 	void showText() {
 		String title = getFirstString();
 		String text = null;
@@ -4263,7 +4275,7 @@ public class Functions implements MacroConstants, Measurements {
 		if (title.equals("Untitled") && text.contains("Test Action Tool"))
 			new MacroInstaller().installSingleTool(text);
 	}
-	
+
 	Variable[] newMenu() {
         String name = getFirstString();
         interp.getComma();
@@ -4277,7 +4289,7 @@ public class Functions implements MacroConstants, Measurements {
     		commands2[i] = new Variable(0, 0.0, commands[i]);
     	return commands2;
 	}
-	
+
 	void setSelectionLocation() {
 		int x = (int)Math.round(getFirstArg());
 		int y = (int)Math.round(getLastArg());
@@ -4288,7 +4300,7 @@ public class Functions implements MacroConstants, Measurements {
 		roi.setLocation(x, y);
 		imp.draw();
 	}
-	
+
 	double is() {
 		boolean state = false;
 		String arg = getStringArg();
@@ -4373,7 +4385,7 @@ public class Functions implements MacroConstants, Measurements {
 			return null;
 		}
 	}
-	
+
 	String doString() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -4402,7 +4414,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Unrecognized String function");
 		return null;
 	}
-	
+
 	private String showString() {
 		showText();
 		return null;
@@ -4429,17 +4441,17 @@ public class Functions implements MacroConstants, Measurements {
 		clipboard.setContents(ss, null);
 		return null;
 	}
-		  
+
 	private String getClipboardContents() {
 		interp.getParens();
 		java.awt.datatransfer.Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
 		Transferable data = clipboard.getContents(null);
 		String s = null;
-		try {s = (String)data.getTransferData(DataFlavor.stringFlavor);} 
+		try {s = (String)data.getTransferData(DataFlavor.stringFlavor);}
 		catch (Exception e) {s = data.toString();}
 		return s;
 	}
-    	
+
 	private String copyResults() {
 		interp.getParens();
 		if (!IJ.isResultsWindow())
@@ -4462,14 +4474,14 @@ public class Functions implements MacroConstants, Measurements {
 			buffer = new StringBuffer(256);
 		return buffer.toString();
 	}
-	
+
 	private void doCommand() {
 		String arg = getStringArg();
 		if (arg.equals("Start Animation"))
 			arg = "Start Animation [\\]";
 		IJ.doCommand(arg);
 	}
-	
+
 	private void getDimensions() {
 		Variable width = getFirstVariable();
 		Variable height = getNextVariable();
@@ -4499,7 +4511,7 @@ public class Functions implements MacroConstants, Measurements {
 			if (IJ.debugMode) IJ.log("  "+i+" "+descriptors[i].name);
 		}
 	}
-	
+
 	String doExt() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -4522,7 +4534,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return desc.dispatch(this);
 	}
-	
+
 	String exec() {
 		String[] cmd;
 		StringBuffer sb = new StringBuffer(256);
@@ -4553,22 +4565,22 @@ public class Functions implements MacroConstants, Measurements {
 			cmd[1] = "/c";
 		}
 		BufferedReader reader = null;
-		try {                                                                                             
-			Process p = Runtime.getRuntime().exec(cmd);   
+		try {
+			Process p = Runtime.getRuntime().exec(cmd);
 			if (openingDoc) return null;
-			reader = new BufferedReader(new InputStreamReader(p.getInputStream()));  
-			String line; int count=1;                                                                                
-			while ((line=reader.readLine())!=null)  {                                              
+			reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+			String line; int count=1;
+			while ((line=reader.readLine())!=null)  {
         		sb.append(line+"\n");
         		if (count++==1&&line.startsWith("Microsoft Windows"))
         			break; // user probably ran 'cmd' without /c option
-        	}                                                             
-		} catch (Exception e) {                                                                         
-    		sb.append(e.getMessage()+"\n");                                                                          
+        	}
+		} catch (Exception e) {
+    		sb.append(e.getMessage()+"\n");
 		} finally {
 			if (reader!=null) try {reader.close();} catch (IOException e) {}
 		}
-		return sb.toString(); 
+		return sb.toString();
 	}
 
 	double getValue() {
@@ -4605,7 +4617,7 @@ public class Functions implements MacroConstants, Measurements {
 			return 0.0;
 		}
 	}
-	
+
 	double getColorValue(Color color) {
 		ImagePlus imp = WindowManager.getCurrentImage();
 		if (imp==null || imp.getBitDepth()==24)
@@ -4632,11 +4644,11 @@ public class Functions implements MacroConstants, Measurements {
 			getDimensions();
 			return Double.NaN;
 		} else if (name.equals("stopOrthoViews")) {
-			interp.getParens(); 
+			interp.getParens();
 			Orthogonal_Views.stop();
 			return Double.NaN;
 		} else if (name.equals("getOrthoViewsID")) {
-			interp.getParens(); 
+			interp.getParens();
 			return Orthogonal_Views.getImageID();
 		} else if (name.equals("setOrthoViews"))
 			return setOrthoViews();
@@ -4688,8 +4700,8 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Unrecognized Stack function");
 		return Double.NaN;
 	}
-	
-	private double setOrthoViews() { 
+
+	private double setOrthoViews() {
 		int x = (int)getFirstArg();
 		int y = (int)getNextArg();
 		int z = (int)getLastArg();
@@ -4698,7 +4710,7 @@ public class Functions implements MacroConstants, Measurements {
 			orthoViews.setCrossLoc(x, y, z);
 		return Double.NaN;
 	}
-	
+
 	void getStackUnits(Calibration cal) {
 		Variable x = getFirstVariable();
 		Variable y = getNextVariable();
@@ -4711,7 +4723,7 @@ public class Functions implements MacroConstants, Measurements {
 		t.setString(cal.getTimeUnit());
 		v.setString(cal.getValueUnit());
 	}
-	
+
 	void getStackStatistics(ImagePlus imp, boolean calibrated) {
 		Variable count = getFirstVariable();
 		Variable mean=null, min=null, max=null, std=null, hist=null;
@@ -4747,7 +4759,7 @@ public class Functions implements MacroConstants, Measurements {
 			hist.setArray(array);
 		}
 	}
-	
+
 	void getActiveChannels(ImagePlus imp) {
 		if (!imp.isComposite())
 			interp.error("Composite image required");
@@ -4789,12 +4801,12 @@ public class Functions implements MacroConstants, Measurements {
 			m = IJ.COLOR;
 		else if (mode.startsWith("gray"))
 			m = IJ.GRAYSCALE;
-		if (m==-1) 
+		if (m==-1)
 			interp.error("Invalid mode");
 		((CompositeImage)imp).setMode(m);
 		imp.updateAndDraw();
 	}
-	
+
 	void swapStackImages(ImagePlus imp) {
 		int n1 = (int)getFirstArg();
 		int n2 = (int)getLastArg();
@@ -4858,7 +4870,7 @@ public class Functions implements MacroConstants, Measurements {
 		img.setDimensions(c, z, t);
 		if (img.getWindow()==null) img.setOpenAsHyperStack(true);
 	}
-	
+
 	void setTool() {
         interp.getLeftParen();
 		if (isStringArg()) {
@@ -4880,14 +4892,14 @@ public class Functions implements MacroConstants, Measurements {
 		if (interp.token!=')') interp.error("')' expected");
 		return s;
 	}
-	
+
 	double matches() {
 		String str = getFirstString();
 		String regex = getLastString();
 		boolean matches = str.matches(regex);
 		return matches?1.0:0.0;
 	}
-	
+
 	void waitForUser() {
 		IJ.wait(50);
 		if (waitForUserDialog!=null && waitForUserDialog.isShowing())
@@ -4913,17 +4925,17 @@ public class Functions implements MacroConstants, Measurements {
 		if (waitForUserDialog.escPressed())
 			throw new RuntimeException(Macro.MACRO_CANCELED);
 	}
-	
+
 	void abortDialog() {
 		if (waitForUserDialog!=null && waitForUserDialog.isVisible())
 			waitForUserDialog.close();
 	}
-	
+
 	double getStringWidth() {
 		resetImage();
 		ImageProcessor ip = getProcessor();
 		setFont(ip);
-		return ip.getStringWidth(getStringArg()); 
+		return ip.getStringWidth(getStringArg());
 	}
 
 	String doList() {
@@ -4959,11 +4971,76 @@ public class Functions implements MacroConstants, Measurements {
 			setMeasurements();
 		else if (name.equals("setCommands"))
 			setCommands();
-		else
+		else if (name.equals("indexOf")) {
+			int index = -1;
+			String key = getStringArg();
+			int size = props.size();
+			String[] keyArr = new String[size];
+			String[] valueArr = new String[size];
+			listToArrays(keyArr, valueArr);
+			for (int i = 0; i < size; i++) {
+				if (keyArr[i].equals(key)) {
+					index = i;
+					break;
+				}
+			}
+			value = "" + index;
+		} else if (name.equals("fromArrays")) {
+			interp.getLeftParen();
+			String[] keys = getStringArray();
+			interp.getComma();
+			String[] values = getStringArray();
+			if (values.length != keys.length) {
+				interp.error("Arrays must have same length");
+			}
+			props.clear();
+			for (int i = 0; i < keys.length; i++) {
+				if (keys[i].equals("")) {
+					interp.error("Key cannot be an empty string");
+				}
+				props.setProperty(keys[i], values[i]);
+			}
+			interp.getRightParen();
+		} else if (name.equals("toArrays")) {
+			Variable keys = getFirstArrayVariable();
+			Variable values = getLastArrayVariable();
+
+			int size = props.size();
+			String[] keyArr = new String[size];
+			String[] valueArr = new String[size];
+
+			listToArrays(keyArr, valueArr);
+			Variable[] keysVar, valuesVar;
+			keysVar = new Variable[size];
+			valuesVar = new Variable[size];
+			for (int i = 0; i < size; i++) {
+				keysVar[i] = new Variable();
+				keysVar[i].setString(keyArr[i]);
+				valuesVar[i] = new Variable();
+				valuesVar[i].setString(valueArr[i]);
+			}
+			keys.setArray(keysVar);
+			values.setArray(valuesVar);
+		} else {
 			interp.error("Unrecognized List function");
+		}
 		return value;
 	}
-	
+
+	void listToArrays(String[] keys, String[] values) {
+		Vector v = new Vector();
+		for (Enumeration en = props.keys(); en.hasMoreElements();) {
+			v.addElement(en.nextElement());
+		}
+		for (int i = 0; i < keys.length; i++) {
+			keys[i] = (String) v.elementAt(i);
+		}
+		Arrays.sort(keys);
+		for (int i = 0; i < keys.length; i++) {
+			values[i] = (String) props.get(keys[i]);
+		}
+	}
+
 	void setCommands() {
 		interp.getParens();
 		Hashtable commands = Menus.getCommands();
@@ -4984,7 +5061,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		props.clear();
 		ImagePlus imp = getImage();
-		int measurements = ALL_STATS;
+		int measurements = ALL_STATS + SLICE;
 		if (arg.contains("limit"))
 			measurements += LIMIT;
 		ImageStatistics stats = imp.getStatistics(measurements);
@@ -5010,7 +5087,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error(""+e);
 		}
 	}
-	
+
 	String getProperties() {
 		interp.getParens();
 		Vector v = new Vector();
@@ -5033,7 +5110,7 @@ public class Functions implements MacroConstants, Measurements {
 	void makePoint() {
 		double x = getFirstArg();
 		double y = getLastArg();
-		if ((int)x==(int)y)
+		if ((int)x==x && (int)y==y)
 			IJ.makePoint((int)x, (int)y);
 		else
 			IJ.makePoint(x, y);
@@ -5055,7 +5132,7 @@ public class Functions implements MacroConstants, Measurements {
 			roi.setAntialiased(antialiasedText);
 		imp.setRoi(roi);
 	}
-	
+
 	void makeEllipse() {
 		ImagePlus imp = getImage();
 		Roi previousRoi = imp.getRoi();
@@ -5073,7 +5150,7 @@ public class Functions implements MacroConstants, Measurements {
 		resetImage();
 		shiftKeyDown = altKeyDown = false;
 	}
-	
+
 	double fit() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -5120,7 +5197,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return Double.NaN;
 	}
-	
+
 	double fitCurve() {
 		interp.getLeftParen();
 		int fit = -1;
@@ -5179,7 +5256,7 @@ public class Functions implements MacroConstants, Measurements {
 		formula.setString(CurveFitter.fList[index]);
 		return Double.NaN;
 	}
-	
+
 	void setMinAndMax() {
 		double min = getFirstArg();
 		double max = getNextArg();
@@ -5193,7 +5270,7 @@ public class Functions implements MacroConstants, Measurements {
 		IJ.setMinAndMax(min, max, channels);
 		resetImage();
 	}
-	
+
 	String debug() {
 		String arg = "break";
 		if (interp.nextToken()=='(')
@@ -5230,7 +5307,7 @@ public class Functions implements MacroConstants, Measurements {
 		IJ.setKeyUp(IJ.ALL_KEYS);
 		return null;
 	}
-	
+
 	Variable[] doArray() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -5279,7 +5356,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Unrecognized Array function");
 		return null;
 	}
-	
+
 	Variable[] fourierArray() {
 		interp.getLeftParen();
 		Variable[] a = getArray();
@@ -5308,7 +5385,7 @@ public class Functions implements MacroConstants, Measurements {
 			a2[i] = new Variable(result[i]);
 		return a2;
 	}
-	
+
 	Variable[] printArray() {
 		String prefix = null;
 		interp.getLeftParen();
@@ -5361,7 +5438,7 @@ public class Functions implements MacroConstants, Measurements {
 				list.add(v);
 				len++;
 			}
-			interp.getToken();				
+			interp.getToken();
 		} while (interp.token==',');
 		Variable[] a2 = new Variable[len];
 		int index = 0;
@@ -5394,21 +5471,21 @@ public class Functions implements MacroConstants, Measurements {
 			a2[i] = (Variable)a[i1++].clone();
 		return a2;
 	}
-	
+
 	Variable[] copyArray() {
 		interp.getLeftParen();
 		Variable[] a = getArray();
 		interp.getRightParen();
 		return duplicate(a);
 	}
-	
+
 	Variable[] duplicate(Variable[] a1) {
 		Variable[] a2 = new Variable[a1.length];
 		for (int i=0; i<a1.length; i++)
 			a2[i] = (Variable)a1[i].clone();
 		return a2;
 	}
-		
+
 	Variable[] trimArray() {
 		interp.getLeftParen();
 		Variable[] a1 = getArray();
@@ -5450,7 +5527,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Mixed strings and numbers");
 		return a;
 	}
-	
+
 	Variable[] getRankPositions() {
 		interp.getLeftParen();
 		Variable[] a = getArray();
@@ -5482,7 +5559,7 @@ public class Functions implements MacroConstants, Measurements {
 			varArray[i] = new Variable((double) indexes[i]);
 		return varArray;
 	}
-    
+
     Variable[] getArrayStatistics() {
 		interp.getLeftParen();
 		Variable[] a = getArray();
@@ -5530,7 +5607,7 @@ public class Functions implements MacroConstants, Measurements {
 			a[i] = new Variable(i);
 		return a;
 	}
-	
+
 	Variable[] fillArray() {
 		interp.getLeftParen();
 		Variable[] a = getArray();
@@ -5539,14 +5616,14 @@ public class Functions implements MacroConstants, Measurements {
 			a[i].setValue(v);
 		return a;
 	}
-	
+
 	Variable[] resampleArray() {
 		interp.getLeftParen();
 		Variable[] a1 = getArray();
 		int len1 = a1.length;
 		int len2 = (int)getLastArg();
-		if (len2<=0)
-			interp.error("Length<=0");
+		if (len1 == 0 || len2<=0)
+			interp.error("Cannot resample from or to zero-length");
 		double[] d1 = new double[len1];
 		for (int i=0; i<len1; i++)
 			d1[i] = a1[i].getValue();
@@ -5561,12 +5638,20 @@ public class Functions implements MacroConstants, Measurements {
 		int len1 = y1.length;
 		double factor =  (double)(len2-1)/(len1-1);
 		double[] y2 = new double[len2];
+		if(len1 == 0){
+		    return y2;
+		}
+		if(len1 == 1){
+		    for (int jj=0; jj<len2; jj++)
+			    y2[jj] = y1[0];
+		    return(y2);
+		}
 		double[] f1 = new double[len1];//fractional positions
 		double[] f2 = new double[len2];
 		for (int jj=0; jj<len1; jj++)
 			f1[jj] = jj*factor;
 		for (int jj=0; jj<len2; jj++)
-			f2[jj] = jj/factor;		
+			f2[jj] = jj/factor;
 		for (int jj=0; jj<len2-1; jj++) {
 			double pos = f2[jj];
 			int leftPos = (int)Math.floor(pos);
@@ -5577,7 +5662,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		y2[len2-1] = y1[len1-1];
 		return y2;
-	}	
+	}
 
 	Variable[] reverseArray() {
 		interp.getLeftParen();
@@ -5591,7 +5676,7 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		return a;
 	}
-	
+
 	Variable[] rotateArray() {
 		interp.getLeftParen();
 		Variable[] a = getArray();
@@ -5636,7 +5721,7 @@ public class Functions implements MacroConstants, Measurements {
 			a2[i] = new Variable(maxima[i]);
 		return a2;
 	}
-	
+
 	Variable[] getVertexAngles() {
 		interp.getLeftParen();
 		Variable[] xx = getArray();
@@ -5732,14 +5817,14 @@ public class Functions implements MacroConstants, Measurements {
      	rt.show(title);
 		return null;
 	}
-	
+
 	double charCodeAt() {
 		String str = getFirstString();
 		int index = (int)getLastArg();
 		checkIndex(index, 0, str.length()-1);
 		return str.charAt(index);
 	}
-	
+
 	void doWand() {
 		int x = (int)getFirstArg();
 		int y = (int)getNextArg();
@@ -5753,7 +5838,7 @@ public class Functions implements MacroConstants, Measurements {
 		IJ.doWand(x, y, tolerance, mode);
 		resetImage();
 	}
-	
+
 	private String ijCall() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -5786,7 +5871,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Unrecognized IJ function name");
 		return null;
 	}
-	
+
 	private void renameResults() {
 		String arg1 = getFirstString();
 		String arg2 = null;
@@ -5805,7 +5890,7 @@ public class Functions implements MacroConstants, Measurements {
 		else
 			IJ.renameResults(arg1);
 	}
-	
+
 	double overlay() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -5915,7 +6000,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Unrecognized function name");
 		return Double.NaN;
 	}
-	
+
 	double overlayAddSelection(ImagePlus imp, Overlay overlay) {
 		String strokeColor = null;
 		double strokeWidth = Double.NaN;
@@ -5952,7 +6037,7 @@ public class Functions implements MacroConstants, Measurements {
 		imp.setOverlay(overlay);
 		return Double.NaN;
 	}
-	
+
 	double overlaySetPosition(Overlay overlay) {
 		int c=0, z=0, t=0;
 		int nargs = 1;
@@ -6002,7 +6087,7 @@ public class Functions implements MacroConstants, Measurements {
 		overlayPath.moveTo(x, y);
 		return Double.NaN;
 	}
-	
+
 	double overlayLineTo() {
 		if (overlayPath==null) overlayPath = new GeneralPath();
 		interp.getLeftParen();
@@ -6029,7 +6114,7 @@ public class Functions implements MacroConstants, Measurements {
 		overlayPath.lineTo(x2, y2);
 		return Double.NaN;
 	}
-	
+
 	double overlayDrawRectOrEllipse(ImagePlus imp, boolean ellipse) {
 		addDrawingToOverlay(imp);
 		float x = (float)Math.round(getFirstArg());
@@ -6067,13 +6152,13 @@ public class Functions implements MacroConstants, Measurements {
 		addRoi(imp, roi);
 		return Double.NaN;
 	}
-	
+
 	double addDrawing(ImagePlus imp) {
 		interp.getParens();
 		addDrawingToOverlay(imp);
 		return Double.NaN;
 	}
-	
+
 	void addDrawingToOverlay(ImagePlus imp) {
 		if (overlayPath==null) return;
 		Roi roi = new ShapeRoi(overlayPath);
@@ -6093,7 +6178,7 @@ public class Functions implements MacroConstants, Measurements {
 		roi.setLineWidth(getProcessor().getLineWidth());
 		overlay.add(roi);
 	}
-	
+
 	double showOverlay(ImagePlus imp) {
 		interp.getParens();
 		addDrawingToOverlay(imp);
@@ -6104,7 +6189,7 @@ public class Functions implements MacroConstants, Measurements {
 			imp.setHideOverlay(false);
 		return Double.NaN;
 	}
-	
+
 	double hideOverlay(ImagePlus imp) {
 		interp.getParens();
 		imp.setHideOverlay(true);
@@ -6117,7 +6202,7 @@ public class Functions implements MacroConstants, Measurements {
 		offscreenOverlay = null;
 		return Double.NaN;
 	}
-	
+
 	double clearOverlay(ImagePlus imp) {
 		interp.getParens();
 		offscreenOverlay = null;
@@ -6151,13 +6236,13 @@ public class Functions implements MacroConstants, Measurements {
 		w.setValue(r.width);
 		h.setValue(r.height);
 	}
-	
+
 	void toScaled() {       //pixel coordinates to calibrated coordinates
 		ImagePlus imp = getImage();
 		Plot plot = (Plot)(getImage().getProperty(Plot.PROPERTY_KEY)); //null if not a plot window
 		int height = imp.getHeight();
 		Calibration cal = imp.getCalibration();
-		
+
 		interp.getLeftParen();
 		if (isArrayArg()) {
 			Variable[] x = getArray();
@@ -6222,7 +6307,7 @@ public class Functions implements MacroConstants, Measurements {
 			}
 		}
 	}
-	
+
 	String doRoi() {
 		interp.getToken();
 		if (interp.token!='.')
@@ -6313,8 +6398,8 @@ public class Functions implements MacroConstants, Measurements {
 			if (interp.nextToken()==',') {
 				interp.getComma();
 				value = getString();
-			} 
-			interp.getRightParen();	
+			}
+			interp.getRightParen();
 			roi.setProperty(key, value);
 			return null;
 		} else if (name.equals("getType")) {
@@ -6329,7 +6414,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.error("Unrecognized Roi function");
 		return null;
 	}
-	
+
 	/*
 	private String getRoiPosition(Roi roi) {
 		Variable channel = getFirstVariable();
@@ -6411,10 +6496,10 @@ public class Functions implements MacroConstants, Measurements {
 		yCoordinates.setArray(ya);
 		return null;
 	}
-		
+
 	private String setSplineAnchors(ImagePlus imp, boolean polyline) {
 		double[] x = getFirstArray();
-		int n = x.length;		
+		int n = x.length;
 		double[] y = getLastArray();
 		if (y.length!=n)
 			interp.error("Arrays are not the same length");
@@ -6433,6 +6518,6 @@ public class Functions implements MacroConstants, Measurements {
 		imp.setRoi(roi);
 		return null;
 	}
-	
+
 } // class Functions
 
diff --git a/ij/macro/Interpreter.java b/ij/macro/Interpreter.java
index d30708d..a1c3371 100644
--- a/ij/macro/Interpreter.java
+++ b/ij/macro/Interpreter.java
@@ -145,7 +145,6 @@ public class Interpreter implements MacroConstants {
 		pc = macroLoc-1;
 		previousInstance = instance;
 		instance = this;
-		//IJ.showStatus("interpreting");
 		pushGlobals();
 		if (func==null)
 			func = new Functions(this, pgm);
@@ -233,8 +232,10 @@ public class Interpreter implements MacroConstants {
 
 	final void doStatement() {
 		getToken();
-		if (debugMode!=Debugger.NOT_DEBUGGING && debugger!=null && !done && token!=';' && token!=FUNCTION)
+		if (debugMode!=Debugger.NOT_DEBUGGING && debugger!=null && !done && token!=';' && token!=FUNCTION) {
 			debugger.debug(this, debugMode);
+			if (done) return;
+		}
 		switch (token) {
 			case VAR:
 				doVar();
diff --git a/ij/macro/MacroConstants.java b/ij/macro/MacroConstants.java
index ae62cda..7aa256d 100644
--- a/ij/macro/MacroConstants.java
+++ b/ij/macro/MacroConstants.java
@@ -41,7 +41,7 @@ public interface MacroConstants {
 		GET_DATE_AND_TIME=382, SET_METADATA=383, CALCULATOR=384, SET_RGB_WEIGHTS=385, MAKE_POLYGON=386, SET_SELECTION_NAME=387,
 		DRAW_RECT=388, DRAW_OVAL=389, FILL_OVAL=390, SET_OPTION=391, SHOW_TEXT=392, SET_SELECTION_LOC=393, GET_DIMENSIONS=394,
 		WAIT_FOR_USER=395, MAKE_POINT=396, MAKE_TEXT=397, MAKE_ELLIPSE=398, GET_DISPLAYED_AREA=399,
-		TO_SCALED=400, TO_UNSCALED=401, MAKE_ARROW=402;
+		TO_SCALED=400, TO_UNSCALED=401, MAKE_ARROW=402, MAKE_ROTATED_RECT=403;
 	static final String[] functions = {"run","invert","selectWindow","wait", "beep", "resetMinAndMax", "resetThreshold",
 		"print", "write", "doWand", "setMinAndMax", "setThreshold", "setTool",
 		"setForegroundColor", "setBackgroundColor", "makeLine", "makeOval", "makeRectangle",
@@ -58,7 +58,7 @@ public interface MacroConstants {
 		"getDateAndTime", "setMetadata", "imageCalculator", "setRGBWeights", "makePolygon", "setSelectionName",
 		"drawRect", "drawOval", "fillOval", "setOption", "showText", "setSelectionLocation", "getDimensions",
 		"waitForUser", "makePoint", "makeText", "makeEllipse", "getDisplayedArea",
-		"toScaled", "toUnscaled", "makeArrow"};
+		"toScaled", "toUnscaled", "makeArrow", "makeRotatedRectangle"};
 	static final int[] functionIDs = {RUN, INVERT, SELECT, WAIT, BEEP, RESET_MIN_MAX, RESET_THRESHOLD,
 		PRINT, WRITE, DO_WAND, SET_MIN_MAX, SET_THRESHOLD, SET_TOOL,
 		SET_FOREGROUND, SET_BACKGROUND, MAKE_LINE, MAKE_OVAL, MAKE_RECTANGLE,
@@ -75,7 +75,7 @@ public interface MacroConstants {
 		GET_DATE_AND_TIME, SET_METADATA, CALCULATOR, SET_RGB_WEIGHTS, MAKE_POLYGON, SET_SELECTION_NAME,
 		DRAW_RECT, DRAW_OVAL, FILL_OVAL, SET_OPTION, SHOW_TEXT, SET_SELECTION_LOC, GET_DIMENSIONS,
 		WAIT_FOR_USER, MAKE_POINT, MAKE_TEXT, MAKE_ELLIPSE, GET_DISPLAYED_AREA,
-		TO_SCALED, TO_UNSCALED, MAKE_ARROW};
+		TO_SCALED, TO_UNSCALED, MAKE_ARROW, MAKE_ROTATED_RECT};
 
 	// Numeric functions
 	static final int GET_PIXEL=1000, ABS=1001, COS=1002, EXP=1003, FLOOR=1004, LOG=1005, MAX_OF=1006, MIN_OF=1007, POW=1008,
diff --git a/ij/measure/ResultsTable.java b/ij/measure/ResultsTable.java
index 8554270..38aa953 100644
--- a/ij/measure/ResultsTable.java
+++ b/ij/measure/ResultsTable.java
@@ -957,7 +957,7 @@ public class ResultsTable implements Cloneable {
 			for (int i=0; i<headings.length; i++)
 				headings[i] = "C"+(i+1);
 		}
-		int firstColumn = headings[0].equals(" ")?1:0;
+		int firstColumn = headings.length>0&&headings[0].equals(" ")?1:0;
 		for (int i=0; i<headings.length; i++) {
 			headings[i] = headings[i].trim();
 			if (commasReplaced) {
diff --git a/ij/plugin/BrowserLauncher.java b/ij/plugin/BrowserLauncher.java
index 66b7ea3..68cbf57 100644
--- a/ij/plugin/BrowserLauncher.java
+++ b/ij/plugin/BrowserLauncher.java
@@ -9,7 +9,6 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URLClassLoader;
-import java.applet.Applet;
 
 /**
  * This plugin implements the File/Import/URL command and the commands in the Help menu that 
@@ -63,13 +62,6 @@ public class BrowserLauncher implements PlugIn {
 		if (error) return;
 		if (theURL==null || theURL.equals(""))
 			theURL = IJ.URL;
-		Applet applet = IJ.getApplet();
-		if (applet!=null) {
-			try {
-				applet.getAppletContext().showDocument(new URL(theURL), "_blank" );
-			} catch (Exception e) {}
-			return;
-		}
 		try {openURL(theURL);}
 		catch (IOException e) {}
 	}
@@ -83,7 +75,7 @@ public class BrowserLauncher implements PlugIn {
 		String errorMessage = "";
 		if (IJ.isMacOSX()) {
 			if (IJ.isJava16())
-				IJ.runMacro("exec('open', '"+url+"')");
+				IJ.runMacro("exec('open', getArgument())",url);
 			else {
 				try {
 					Method aMethod = mrjFileUtilsClass.getDeclaredMethod("sharedWorkspace", new Class[] {});
@@ -134,7 +126,7 @@ public class BrowserLauncher implements PlugIn {
 	 * required at runtime to locate the user's web browser.
 	 */
 	private static void loadClasses() {
-		if (IJ.isMacOSX() && !IJ.isJava16() && IJ.getApplet()==null) {
+		if (IJ.isMacOSX() && !IJ.isJava16()) {
 			try {
 				if (new File("/System/Library/Java/com/apple/cocoa/application/NSWorkspace.class").exists()) {
 					ClassLoader classLoader = new URLClassLoader(new URL[]{new File("/System/Library/Java").toURL()});
diff --git a/ij/plugin/CalibrationBar.java b/ij/plugin/CalibrationBar.java
index 55737b7..71b836d 100644
--- a/ij/plugin/CalibrationBar.java
+++ b/ij/plugin/CalibrationBar.java
@@ -56,6 +56,7 @@ public class CalibrationBar implements PlugIn {
 	int userPadding = 0;
 	int fontHeight = 0;
 	boolean boldText;
+	boolean showUnit;
 	static boolean staticFlatten;
 	boolean flatten = staticFlatten;
 	Object backupPixels;
@@ -151,10 +152,10 @@ public class CalibrationBar implements PlugIn {
 		gd.addNumericField("Decimal places:", decimalPlaces, 0);
 		gd.addNumericField("Font size:", fontSize, 0);
 		gd.addNumericField("Zoom factor:", zoom, 1);
-		String[] labels = {"Bold text", "Overlay"};
-		boolean[] states = {boldText, !flatten};
+		String[] labels = {"Bold text", "Overlay", "Show unit"};
+		boolean[] states = {boldText, !flatten, showUnit};
 		gd.setInsets(10, 30, 0);
-		gd.addCheckboxGroup(1, 2, labels, states);
+		gd.addCheckboxGroup(2, 2, labels, states);
 		gd.showDialog();
 		if (gd.wasCanceled())
 			return false;
@@ -167,6 +168,7 @@ public class CalibrationBar implements PlugIn {
 		zoom = (double)gd.getNextNumber();
 		boldText = gd.getNextBoolean();
 		flatten = !gd.getNextBoolean();
+		showUnit = gd.getNextBoolean();
 		if (!IJ.isMacro())
 			staticFlatten = flatten;
 		return true;
@@ -300,7 +302,9 @@ public class CalibrationBar implements PlugIn {
 			double yLabelD = (int)(YMARGIN*zoom + BAR_LENGTH*zoom - i*barStep - 1);
 			int yLabel = (int)(Math.round( y + BAR_LENGTH*zoom - i*barStep - 1));
 			Calibration cal = imp.getCalibration();
-			//s = cal.getValueUnit();
+			String s = "";
+			if (showUnit)
+				s = cal.getValueUnit();
 			ImageProcessor ipOrig = imp.getProcessor();
 			double min = ipOrig.getMin();
 			double max = ipOrig.getMax();
@@ -316,18 +320,19 @@ public class CalibrationBar implements PlugIn {
 				if (!decimalPlacesChanged && decimalPlaces==0 && ((int)cmax!=cmax||(int)cmin!=cmin))
 					decimalPlaces = 2;
 			}
+			String todisplay = d2s(grayLabel)+" "+s;
 			if (overlay!=null) {
-				TextRoi label = new TextRoi(d2s(grayLabel), x + 5, yLabel + fontHeight/2, font);
+				TextRoi label = new TextRoi(todisplay, x + 5, yLabel + fontHeight/2, font);				
 				label.setStrokeColor(c);
 				overlay.add(label, CALIBRATION_BAR);
 			}
-			int iLength = metrics.stringWidth(d2s(grayLabel));
+			int iLength = metrics.stringWidth(todisplay);
 			if (iLength > maxLength)
-				maxLength = iLength;
+				maxLength = iLength+5;
 		}
 		return maxLength;
 	}
-	
+		
 	String d2s(double d) {
 			return IJ.d2s(d,decimalPlaces);
 	}
@@ -441,6 +446,7 @@ public class CalibrationBar implements PlugIn {
 			textColor = ( (Choice)(choice.elementAt(2)) ).getSelectedItem();
 			boldText = ( (Checkbox)(checkbox.elementAt(0)) ).getState();
 			flatten = !( (Checkbox)(checkbox.elementAt(1)) ).getState();
+			showUnit = ( (Checkbox)(checkbox.elementAt(2)) ).getState();
 			updateColorBar();
 		}
 
diff --git a/ij/plugin/Duplicator.java b/ij/plugin/Duplicator.java
index f4c4fcb..3747b42 100644
--- a/ij/plugin/Duplicator.java
+++ b/ij/plugin/Duplicator.java
@@ -35,6 +35,13 @@ public class Duplicator implements PlugIn, TextListener, ItemListener {
 
 	public void run(String arg) {
 		imp = IJ.getImage();
+		Roi roiA = imp.getRoi();
+		ImagePlus impA = imp;
+		boolean isRotatedRect = (roiA!=null &&  roiA instanceof RotatedRectRoi);
+		if (isRotatedRect) {
+			Rectangle bounds = imp.getRoi().getBounds();
+			imp.setRoi(bounds);
+		}
 		int stackSize = imp.getStackSize();
 		String title = imp.getTitle();
 		String newTitle = WindowManager.getUniqueName(title);
@@ -42,7 +49,10 @@ public class Duplicator implements PlugIn, TextListener, ItemListener {
 		duplicateStack = staticDuplicateStack && !IJ.isMacro();
 		if (!IJ.altKeyDown()||stackSize>1) {
 			if (imp.isHyperStack() || imp.isComposite()) {
-				duplicateHyperstack(imp, newTitle);
+				duplicateHyperstack(imp, newTitle);			
+				if (isRotatedRect) {
+					straightenRotatedRect(impA, roiA, IJ.getImage());	
+				}								
 				return;
 			} else
 				newTitle = showDialog(imp, "Duplicate...", "Title: ");
@@ -50,18 +60,18 @@ public class Duplicator implements PlugIn, TextListener, ItemListener {
 		if (newTitle==null)
 			return;
 		ImagePlus imp2;
-		Roi roi = imp.getRoi();
-		if (duplicateStack && (first>1||last<stackSize))
-			imp2 = run(imp, first, last);
-		else if (duplicateStack || imp.getStackSize()==1)
-			imp2 = run(imp);
-		else
-			imp2 = crop(imp);
-		Calibration cal = imp2.getCalibration();
-		if (roi!=null && (cal.xOrigin!=0.0||cal.yOrigin!=0.0)) {
-			cal.xOrigin -= roi.getBounds().x;
-			cal.yOrigin -= roi.getBounds().y;
-		}
+		Roi roi = imp.getRoi();		
+			if (duplicateStack && (first>1||last<stackSize))
+				imp2 = run(imp, first, last);
+			else if (duplicateStack || imp.getStackSize()==1)
+				imp2 = run(imp);
+			else
+				imp2 = crop(imp);
+			Calibration cal = imp2.getCalibration();
+			if (roi!=null && (cal.xOrigin!=0.0||cal.yOrigin!=0.0)) {
+				cal.xOrigin -= roi.getBounds().x;
+				cal.yOrigin -= roi.getBounds().y;
+			}	
 		imp2.setTitle(newTitle);
 		if (roi!=null && roi.isArea() && roi.getType()!=Roi.RECTANGLE) {
 			Roi roi2 = (Roi)cropRoi(imp, roi).clone();
@@ -71,8 +81,95 @@ public class Duplicator implements PlugIn, TextListener, ItemListener {
 		imp2.show();
 		if (stackSize>1 && imp2.getStackSize()==stackSize)
 			imp2.setSlice(imp.getCurrentSlice());
+		if (isRotatedRect)
+			straightenRotatedRect(impA, roiA, imp2);		
 	}
-                
+	
+ /** Rotates duplicated part of image
+	- impA is original image,
+	- roiA is orig rotatedRect
+	- impB contains duplicated overlapping bounding rectangle	
+	processing steps:
+	- increase canvas of impB before rotation
+	- rotate impB
+	- calculate excentricity
+	- translate to compensate excentricity 
+	- create orthogonal rectangle in center
+	- crop to impC	
+	Author: N. Vischer
+	*/
+	private void straightenRotatedRect(ImagePlus impA, Roi roiA, ImagePlus impB) {
+		impB.deleteRoi();//we have it in roiA
+		String title = impB.getTitle();
+		if(impB.getOverlay() != null)
+			impB.getOverlay().clear();
+		int boundLeft = roiA.getBounds().x;
+		int boundTop = roiA.getBounds().y;
+		int boundWidth = roiA.getBounds().width;
+		int boundHeight = roiA.getBounds().height;
+
+		float[] xx = roiA.getFloatPolygon().xpoints;
+		float[] yy = roiA.getFloatPolygon().ypoints;
+
+		double dx1 = xx[1] - xx[0];//calc sides and angle
+		double dy1 = yy[1] - yy[0];
+		double dx2 = xx[2] - xx[1];
+		double dy2 = yy[2] - yy[1];
+
+		double rrWidth = Math.sqrt(dx1 * dx1 + dy1 * dy1);//width of rot rect
+		double rrHeight = Math.sqrt(dx2 * dx2 + dy2 * dy2);
+		double rrDia = Math.sqrt(rrWidth * rrWidth + rrHeight * rrHeight);
+
+		double phi1 = -Math.atan2(dy1, dx1);
+		double phi0 = phi1 * 180 / Math.PI;
+
+		double usedL = Math.max(boundLeft, 0); //usedrect is orthogonal rect to be rotated
+		double usedR = Math.min(boundLeft + boundWidth, impA.getWidth());
+		double usedT = Math.max(boundTop, 0);
+		double usedB = Math.min(boundTop + boundHeight, impA.getHeight());
+		double usedCX = (usedL + usedR) / 2;
+		double usedCY = (usedT + usedB) / 2; //Center of UsedRect
+
+		double boundsCX = boundLeft + boundWidth / 2;//Center of Bound = center of RotRect
+		double boundsCY = boundTop + boundHeight / 2;
+
+		double dx3 = boundsCX - usedCX;//calculate excentricity
+		double dy3 = boundsCY - usedCY;
+		double rad3 = Math.sqrt(dx3 * dx3 + dy3 * dy3);
+		double phi3 = Math.atan2(dy3, dx3);
+		double phi4 = phi3 + phi1;
+		double dx4 = -rad3 * Math.cos(phi4);
+		double dy4 = -rad3 * Math.sin(phi4);
+
+		//Increase canvas to a square large enough for rotation
+		ImageStack stackOld = impB.getStack();
+		int currentSlice = impB.getCurrentSlice();
+		double xOff = (rrDia - (usedR - usedL)) / 2;//put img in center
+		double yOff = (rrDia - (usedB - usedT)) / 2;
+
+		ImageStack stackNew = (new CanvasResizer()).expandStack(stackOld, (int) rrDia, (int) rrDia, (int) xOff, (int) yOff);
+		impB.setStack(stackNew);
+		ImageProcessor ip = impB.getProcessor();
+		ip.setInterpolationMethod(ImageProcessor.BILINEAR);
+		ip.setBackgroundValue(0);
+
+		for (int slc = 0; slc < stackNew.size(); slc++) {
+			impB.setSlice(slc+1);
+			ip.rotate(phi0); //Rotate
+			ip.translate(dx4, dy4); //Translate
+		}
+
+		int x = (impB.getWidth() - (int) rrWidth) / 2;
+		int y = (impB.getHeight() - (int) rrHeight) / 2;
+
+		impB.setStack(impB.getStack().crop(x, y, 0, (int) rrWidth, (int) rrHeight, impB.getStack().getSize()));//Crop
+		impB.setSlice(currentSlice);
+		impB.setTitle(title);
+		impB.show();
+		impB.updateAndDraw();
+		impA.setRoi(roiA);//restore rotated rect in source image
+	}	
+	                
 	/** Returns a copy of the image, stack or hyperstack contained in the specified ImagePlus.
 	* @see ij.ImagePlus#duplicate
 	*/
diff --git a/ij/plugin/FFT.java b/ij/plugin/FFT.java
index dafc9ce..ab664b2 100644
--- a/ij/plugin/FFT.java
+++ b/ij/plugin/FFT.java
@@ -38,7 +38,10 @@ public class FFT implements  PlugIn, Measurements {
     public void run(String arg) {
         if (arg.equals("options")) {
             showDialog();
-            if (doFFT) arg="fft"; else return;
+            if (doFFT)
+            	arg="fft";
+            else
+            	return;
         }
         imp = IJ.getImage();
         if (arg.equals("fft") && imp.isComposite()) {
@@ -126,12 +129,13 @@ public class FFT implements  PlugIn, Measurements {
         showStatus("Forward transform");
         fht.transform();
         showStatus("Calculating power spectrum");
+        long t0 = System.currentTimeMillis();
         ImageProcessor ps = fht.getPowerSpectrum();
         if (!(displayFHT||displayComplex||displayRawPS))
         	displayFFT = true;
         if (displayFFT) {
             ImagePlus imp2 = new ImagePlus("FFT of "+imp.getTitle(), ps);
-            imp2.show();
+            imp2.show((System.currentTimeMillis()-t0)+" ms");
             imp2.setProperty("FHT", fht);
             imp2.setCalibration(imp.getCalibration());
             String properties = "Fast Hartley Transform\n";
@@ -359,7 +363,7 @@ public class FFT implements  PlugIn, Measurements {
     }
     
     /** Complex to Complex Inverse Fourier Transform
-    *   @author Joachim Wesner
+    *   Author: Joachim Wesner
     */
     void c2c2DFFT(float[] rein, float[] imin, int maxN, float[] reout, float[] imout) {
             FHT fht = new FHT(new FloatProcessor(maxN,maxN));
@@ -380,7 +384,7 @@ public class FFT implements  PlugIn, Measurements {
       }
 
     /** Build FHT input for equivalent inverse FFT
-    *   @author Joachim Wesner
+    *   Author: Joachim Wesner
     */
     void cplxFHT(int row, int maxN, float[] re, float[] im, boolean reim, float[] fht) {
             int base = row*maxN;
diff --git a/ij/plugin/GelAnalyzer.java b/ij/plugin/GelAnalyzer.java
index c95bc63..ae79144 100644
--- a/ij/plugin/GelAnalyzer.java
+++ b/ij/plugin/GelAnalyzer.java
@@ -227,11 +227,14 @@ public class GelAnalyzer implements PlugIn {
 		if (nLanes<MAX_LANES)
 			nLanes += 1;
 		IJ.showStatus("Lane " + nLanes + " selected");
-
-		if(isVertical)
+		if (isVertical)
 			x[nLanes] = rect.x;
 		else
 			x[nLanes] = rect.y;
+		if(x[nLanes]==x[nLanes-1]){
+			nLanes--; //avoid duplicate
+			return;
+		}
 		if (isVertical && rect.y!=firstRect.y) {
 			rect.y = firstRect.y;
 			gel.setRoi(rect);
diff --git a/ij/plugin/MacroInstaller.java b/ij/plugin/MacroInstaller.java
index 8e6842b..b3bcbea 100644
--- a/ij/plugin/MacroInstaller.java
+++ b/ij/plugin/MacroInstaller.java
@@ -13,7 +13,7 @@ import ij.plugin.frame.*;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              [...]
 
 /** This plugin implements the Plugins/Macros/Install Macros command. It is also used by the Editor
-	class to install macro in menus and by the ImageJ class to install macros at startup. */
+	class to install macros in menus and by the ImageJ class to install macros at startup. */
 public class MacroInstaller implements PlugIn, MacroConstants, ActionListener {
 
 	public static final int MAX_SIZE = 28000, MAX_MACROS=100, XINC=10, YINC=18;
diff --git a/ij/plugin/NewPlugin.java b/ij/plugin/NewPlugin.java
index d9d5e6b..762d98a 100644
--- a/ij/plugin/NewPlugin.java
+++ b/ij/plugin/NewPlugin.java
@@ -12,8 +12,8 @@ public class NewPlugin implements PlugIn {
 
 	public static final int MACRO=0, JAVASCRIPT=1, PLUGIN=2, PLUGIN_FILTER=3, PLUGIN_FRAME=4,
 		TEXT_FILE=5, TABLE=6, MACRO_TOOL=7, PLUGIN_TOOL=8, TEMPLATE=9;
-    private static int rows = 16;
-    private static int columns = 60;
+    private static int rows = 24;
+    private static int columns = 80;
     private static int tableWidth = 350;
     private static int tableHeight = 250;
     private int type = MACRO;
diff --git a/ij/plugin/RGBStackConverter.java b/ij/plugin/RGBStackConverter.java
index 4a3f154..72218c1 100644
--- a/ij/plugin/RGBStackConverter.java
+++ b/ij/plugin/RGBStackConverter.java
@@ -108,6 +108,8 @@ public class RGBStackConverter implements PlugIn, DialogListener {
 			imp.changes = false;
 			imp.close();
 		}
+		if (imp2.getWindow()!=null)
+			IJ.selectWindow(imp2.getID());
 	}
 
 	public void convertHyperstack(ImagePlus imp, ImagePlus imp2) {
diff --git a/ij/plugin/ScaleBar.java b/ij/plugin/ScaleBar.java
index 051d3fc..cfff273 100644
--- a/ij/plugin/ScaleBar.java
+++ b/ij/plugin/ScaleBar.java
@@ -269,21 +269,31 @@ public class ScaleBar implements PlugIn {
 		int width = imp.getWidth();
 		int height = imp.getHeight();
 		int fraction = 20;
-		int x = width - width/fraction - barWidthInPixels;
+		int fontType = boldText?Font.BOLD:Font.PLAIN;
+		String font = serifFont?"Serif":"SanSerif";
+		ImageProcessor ip = imp.getProcessor();
+		ip.setFont(new Font(font, fontType, fontSize));
+		ip.setAntialiasedText(true);
+		String label = getLength(barWidth)+" "+getUnits(imp);
+		int swidth = hideText?0:ip.getStringWidth(label);
+		int labelWidth = (swidth < barWidthInPixels)?0:(int) (barWidthInPixels-swidth)/2;
+		int x = 0;
 		int y = 0;
-		if (location.equals(locations[UPPER_RIGHT]))
-			 y = height/fraction;
-		else if (location.equals(locations[LOWER_RIGHT]))
+		if (location.equals(locations[UPPER_RIGHT])) {
+			x = width - width/fraction - barWidthInPixels + labelWidth;
+			y = height/fraction;
+		} else if (location.equals(locations[LOWER_RIGHT])) {
+			x = width - width/fraction - barWidthInPixels + labelWidth;
 			y = height - height/fraction - barHeightInPixels - fontSize;
-		else if (location.equals(locations[UPPER_LEFT])) {
-			x = width/fraction;
+		} else if (location.equals(locations[UPPER_LEFT])) {
+			x = width/fraction - labelWidth;
 			y = height/fraction;
 		} else if (location.equals(locations[LOWER_LEFT])) {
-			x = width/fraction;
+			x = width/fraction - labelWidth;
 			y = height - height/fraction - barHeightInPixels - fontSize;
 		} else {
 			if (roiX==-1)
-				 return false;
+				return false;
 			x = roiX;
 			y = roiY;
 		}
diff --git a/ij/plugin/Selection.java b/ij/plugin/Selection.java
index 3f6235a..5616c65 100644
--- a/ij/plugin/Selection.java
+++ b/ij/plugin/Selection.java
@@ -224,7 +224,7 @@ public class Selection implements PlugIn, Measurements {
 		int type = roi.getType();
 		boolean segmentedSelection = type==Roi.POLYGON||type==Roi.POLYLINE;
 		if (!(segmentedSelection||type==Roi.FREEROI||type==Roi.TRACED_ROI||type==Roi.FREELINE))
-			{IJ.error("Spline", "Polygon or polyline selection required"); return;}
+			{IJ.error("Spline Fit", "Polygon or polyline selection required"); return;}
 		if (roi instanceof EllipseRoi)
 			return;
 		PolygonRoi p = (PolygonRoi)roi;
diff --git a/ij/plugin/StackPlotter.java b/ij/plugin/StackPlotter.java
new file mode 100644
index 0000000..0cddaf1
--- /dev/null
+++ b/ij/plugin/StackPlotter.java
@@ -0,0 +1,93 @@
+/**
+ * This plugin, which implements the Image/Stacks/Plot XY Profile command,
+ * generates a stack of plots with the same vertical scale.
+ * Source image is a stack or hyperstack.
+ * Line or rectangle selection is required.
+ * @author Jerome Parent
+*/
+
+package ij.plugin;
+import ij.*;
+import ij.gui.*;
+import java.awt.*;
+
+public class StackPlotter implements PlugIn {
+	
+	private int channel = 1;
+	private int slice = 1;
+	private int frame = 1;
+	private int frames = 1;
+	
+	public void run(String arg) {
+		ImagePlus imp = IJ.getImage();
+		//Check if Roi is defined
+		if (imp.getRoi() == null) {
+			IJ.error("Stack Plotter", "Line or rectangular selection required");
+			return;
+		}
+		//Check if Image is a Stack
+		int dim = imp.getNDimensions();
+		if (dim < 3) {
+			IJ.error("Stack Plotter","This plugin requires a stack");
+			return;
+		}
+		//Get Stack size
+		int length = 0;
+		if (dim==3)
+			length = imp.getImageStackSize();
+		// Plot stack over frames information, improvement will be to select the dimension to plot over
+		boolean plotFrames = true;
+		if (dim>3) {
+			channel = imp.getChannel();
+			slice = imp.getSlice();
+			frame = imp.getFrame();
+			length = frames = imp.getNFrames();
+			if (dim==4 && length==1) {
+				plotFrames = false;
+				length = imp.getNSlices();
+			}
+		} else
+			slice = imp.getCurrentSlice();
+		
+		//Get a profile plot for each frame in the stack
+		//Store min and max value of all Profile across the stack
+		ProfilePlot[] pPlot = new ProfilePlot[length];
+		double ymin = 0;
+		double ymax = 0;
+		for (int i=0; i<length; i++) {
+			if (dim == 3) imp.setPosition(i+1);
+			if (dim > 3) {
+				if (plotFrames)
+					imp.setPosition(channel,slice,i+1);
+				else
+					imp.setPosition(channel,i+1,frame);
+			}
+			pPlot[i] =  new ProfilePlot(imp);
+			if (pPlot[i] == null) return;
+			if (pPlot[i].getMin() < ymin) ymin = pPlot[i].getMin();
+			if (pPlot[i].getMax() > ymax) ymax = pPlot[i].getMax();
+		}
+		//Save current Min and Max values of profile plot
+		double pp_min = ProfilePlot.getFixedMin();
+		double pp_max = ProfilePlot.getFixedMax();
+		//Set same Min Max values for all plots
+		ProfilePlot.setMinAndMax(ymin,ymax);
+		
+		//Make a profile stack
+		Plot plot = pPlot[0].getPlot();
+		Dimension size = plot.getSize();
+		ImageStack stack = new ImageStack(size.width,size.height);
+		for (int i=0; i< length; i++) {
+			plot = pPlot[i].getPlot();
+			stack.addSlice(plot.getProcessor());
+		}
+		ImagePlus output = new ImagePlus("Profile Plots",stack);
+		output.show();
+		output.setSlice(slice);
+		if (dim==3) imp.setPosition(slice);
+		if (dim>3) imp.setPosition(channel,slice,frame);
+		//reset profile plot Min and May
+		ProfilePlot.setMinAndMax(pp_min,pp_max);
+	}
+	
+}
diff --git a/ij/plugin/Straightener.java b/ij/plugin/Straightener.java
index ff97317..c2950e6 100644
--- a/ij/plugin/Straightener.java
+++ b/ij/plugin/Straightener.java
@@ -12,8 +12,13 @@ public class Straightener implements PlugIn {
  	public void run(String arg) {
 		ImagePlus imp = IJ.getImage();
 		Roi roi = imp.getRoi();
-		if (roi==null || !roi.isLine()) {
-			IJ.error("Straightener", "Line selection required");
+		boolean rotatedRectangle = roi!=null && (roi instanceof RotatedRectRoi);
+		if (roi==null || !(roi.isLine()||rotatedRectangle)) {
+			IJ.error("Straightener", "Line, or rotated rectangle, selection required");
+			return;
+		}
+		if (rotatedRectangle) {
+			IJ.run(imp, "Duplicate...", " ");
 			return;
 		}
 		int width = (int)Math.round(roi.getStrokeWidth());
@@ -56,11 +61,6 @@ public class Straightener implements PlugIn {
 		if (cal.pixelWidth==cal.pixelHeight)
 			imp2.setCalibration(cal);
 		imp2.show();
-		//imp.setRoi(roi);
-		//if (type==Roi.POLYLINE&& !((PolygonRoi)roi).isSplineFit()) {
-		//	((PolygonRoi)roi).fitSpline();
-		//	imp.draw();
-		//}
 		if (isMacro) Line.setWidth(originalWidth);
 	}
 	
diff --git a/ij/plugin/WandToolOptions.java b/ij/plugin/WandToolOptions.java
index 1aa184d..0154224 100644
--- a/ij/plugin/WandToolOptions.java
+++ b/ij/plugin/WandToolOptions.java
@@ -21,7 +21,7 @@ public class WandToolOptions implements PlugIn, DialogListener {
  	public void run(String arg) {
  		imp = WindowManager.getCurrentImage();
  		Roi roi = imp!=null?imp.getRoi():null;
- 		boolean selection = roi!=null && roi.getType()==Roi.TRACED_ROI;
+ 		boolean selection = roi!=null && (roi.getType()==Roi.TRACED_ROI||roi.getType()==Roi.POLYGON);
  		if (imp==null || (ID!=0&&imp.getID()!=ID) || !selection)
  			startx = starty = 0;
  		ID = imp!=null?imp.getID():0;
@@ -31,13 +31,14 @@ public class WandToolOptions implements PlugIn, DialogListener {
  			sliderMax = imp.getProcessor().getMax();
  			if (depth==32) sliderMax+=0.0000000001;
  		}
- 		showCheckbox = imp!=null && depth!=24 && WindowManager.getFrame("Threshold")==null
- 			&& !imp.isThreshold();
+ 		showCheckbox = imp!=null && depth!=24 && WindowManager.getFrame("Threshold")==null && !imp.isThreshold();
 		GenericDialog gd = new GenericDialog("Wand Tool");
 		gd.addSlider("Tolerance: ", 0, sliderMax, tolerance);
 		gd.addChoice("Mode:", modes, mode);
-		if (showCheckbox) {
+		if (showCheckbox)
 			gd.addCheckbox("Enable Thresholding", false);
+		gd.addCheckbox("Smooth if thresholded", Prefs.smoothWand);
+		if (showCheckbox) {
 			gd.setInsets(2,0,0);
 			gd.addMessage("Thresholded objects are traced and \"Tolerance\"\nis ignored when thresholding is enabled.");
 		}
@@ -56,8 +57,9 @@ public class WandToolOptions implements PlugIn, DialogListener {
 				IJ.run("Threshold...");
 			}
 		}
+		Prefs.smoothWand = gd.getNextBoolean();
 		if (startx>0||starty>0)
-			IJ.doWand(startx, starty, tolerance, mode);
+			IJ.doWand(startx, starty, tolerance, mode+(Prefs.smoothWand?" smooth":""));
 		return true;
 	}
 
diff --git a/ij/plugin/ZProjector.java b/ij/plugin/ZProjector.java
index 926b2f5..2d58792 100644
--- a/ij/plugin/ZProjector.java
+++ b/ij/plugin/ZProjector.java
@@ -3,6 +3,7 @@ import ij.*;
 import ij.gui.*; 
 import ij.process.*;
 import ij.plugin.filter.*; 
+import ij.plugin.frame.Recorder;
 import ij.measure.Measurements;
 import java.lang.*; 
 import java.awt.*; 
@@ -49,6 +50,7 @@ public class ZProjector implements PlugIn {
     
     private String color = "";
     private boolean isHyperstack;
+    private boolean simpleComposite;
     private int increment = 1;
     private int sliceCount;
 
@@ -59,6 +61,42 @@ public class ZProjector implements PlugIn {
     public ZProjector(ImagePlus imp) {
 		setImage(imp); 
     }
+    
+    /** Performs projection on the entire stack using the specified method and returns
+    	 the result, where 'method' is "avg", "min", "max", "sum", "sd" or "median".
+    	 Add " all" to 'method' to project all hyperstack time points. */
+    public static ImagePlus run(ImagePlus imp, String method) {
+    	return run(imp, method, 1, imp.getStackSize());
+    }
+
+	/** Performs projection using the specified method and stack range, and returns
+		 the result, where 'method' is "avg", "min", "max", "sum", "sd" or "median".
+		Add " all" to 'method' to project all hyperstack time points. <br>
+		Example: http://imagej.nih.gov/ij/macros/js/ProjectionDemo.js
+	*/
+	 public static ImagePlus run(ImagePlus imp, String method, int startSlice, int stopSlice) {
+    	ZProjector zp = new ZProjector(imp);
+    	zp.setStartSlice(startSlice);
+    	zp.setStopSlice(stopSlice);
+    	zp.isHyperstack = imp.isHyperStack();
+    	if (zp.isHyperstack && startSlice==1 && stopSlice==imp.getStackSize())
+    		zp.setDefaultBounds();
+    	if (method==null) return null;
+    	method = method.toLowerCase();
+    	int m = -1;
+    	if (method.startsWith("av")) m = AVG_METHOD;
+    	else if (method.startsWith("max")) m = MAX_METHOD;
+    	else if (method.startsWith("min")) m = MIN_METHOD;
+    	else if (method.startsWith("sum")) m = SUM_METHOD;
+    	else if (method.startsWith("sd")) m = SD_METHOD;
+    	else if (method.startsWith("median")) m = MEDIAN_METHOD;
+    	if (m<0)
+    		throw new IllegalArgumentException("Invalid projection method: "+method);
+    	zp.allTimeFrames = method.contains("all");
+    	zp.setMethod(m);
+    	zp.doProjection(true);
+    	return zp.getProjection();
+    }
 
     /** Explicitly set image to be projected. This is useful if
 	ZProjection_ object is to be used not as a plugin but as a
@@ -92,14 +130,13 @@ public class ZProjector implements PlugIn {
 
     public void run(String arg) {
 		imp = IJ.getImage();
-		int stackSize = imp.getStackSize();
 		if (imp==null) {
 	    	IJ.noImage(); 
 	    	return; 
 		}
 
 		//  Make sure input image is a stack.
-		if(stackSize==1) {
+		if(imp.getStackSize()==1) {
 	    	IJ.error("Z Project", "Stack required"); 
 	    	return; 
 		}
@@ -110,23 +147,7 @@ public class ZProjector implements PlugIn {
 	    		return; 
 		}
 
-		// Set default bounds.
-		int channels = imp.getNChannels();
-		int frames = imp.getNFrames();
-		int slices = imp.getNSlices();
-		isHyperstack = imp.isHyperStack()||( ij.macro.Interpreter.isBatchMode()&&((frames>1&&frames<stackSize)||(slices>1&&slices<stackSize)));
-		boolean simpleComposite = channels==stackSize;
-		if (simpleComposite)
-			isHyperstack = false;
-		startSlice = 1; 
-		if (isHyperstack) {
-			int nSlices = imp.getNSlices();
-			if (nSlices>1)
-				stopSlice = nSlices;
-			else
-				stopSlice = imp.getNFrames();
-		} else
-			stopSlice  = stackSize;
+		setDefaultBounds();
 			
 		// Build control dialog
 		GenericDialog gd = buildControlDialog(startSlice,stopSlice);
@@ -136,29 +157,70 @@ public class ZProjector implements PlugIn {
 		if (!imp.lock()) return;   // exit if in use
 		long tstart = System.currentTimeMillis();
 		gd.setSmartRecording(true);
+		int startSlice2 = startSlice;
+		int stopSlice2 = stopSlice;
 		setStartSlice((int)gd.getNextNumber());
 		setStopSlice((int)gd.getNextNumber()); 
+		boolean rangeChanged = startSlice!=startSlice2 || stopSlice!=stopSlice2;
+		startSlice2 = startSlice;
+		stopSlice2 = stopSlice;
 		gd.setSmartRecording(false);
 		method = gd.getNextChoiceIndex();
 		Prefs.set(METHOD_KEY, method);
-		if (isHyperstack) {
+		if (isHyperstack)
 			allTimeFrames = imp.getNFrames()>1&&imp.getNSlices()>1?gd.getNextBoolean():false;
-			doHyperStackProjection(allTimeFrames);
-		} else if (imp.getType()==ImagePlus.COLOR_RGB)
-			doRGBProjection(true);
-		else 
-			doProjection(true); 
+		doProjection(true); 
 
 		if (arg.equals("") && projImage!=null) {
 			long tstop = System.currentTimeMillis();
-			projImage.setCalibration(imp.getCalibration());
 			if (simpleComposite) IJ.run(projImage, "Grays", "");
 			projImage.show("ZProjector: " +IJ.d2s((tstop-tstart)/1000.0,2)+" seconds");
 		}
 
 		imp.unlock();
 		IJ.register(ZProjector.class);
-		return; 
+		if (Recorder.scriptMode()) {
+			String m = getMethodAsString();
+			if (isHyperstack && allTimeFrames)
+				m = m + " all";
+			String range = "";
+			if (rangeChanged)
+				range = ","+startSlice2+","+stopSlice2;
+			Recorder.recordCall("imp = ZProjector.run(imp,\""+m+"\""+range+");");
+		}
+		
+    }
+    
+    private String getMethodAsString() {
+    	switch (method) {
+     		case AVG_METHOD: return "avg";
+    		case MAX_METHOD: return "max";
+    		case MIN_METHOD: return "min";
+    		case SUM_METHOD: return "sum";
+    		case SD_METHOD: return "sd";
+    		case MEDIAN_METHOD: return "median";
+    		default: return "avg";
+    	}
+    }
+    
+    private void setDefaultBounds() {
+		int stackSize = imp.getStackSize();
+    	int channels = imp.getNChannels();
+		int frames = imp.getNFrames();
+		int slices = imp.getNSlices();
+		isHyperstack = imp.isHyperStack()||( ij.macro.Interpreter.isBatchMode()&&((frames>1&&frames<stackSize)||(slices>1&&slices<stackSize)));
+		simpleComposite = channels==stackSize;
+		if (simpleComposite)
+			isHyperstack = false;
+		startSlice = 1; 
+		if (isHyperstack) {
+			int nSlices = imp.getNSlices();
+			if (nSlices>1)
+				stopSlice = nSlices;
+			else
+				stopSlice = imp.getNFrames();
+		} else
+			stopSlice  = stackSize;
     }
     
     public void doRGBProjection() {
@@ -227,6 +289,10 @@ public class ZProjector implements PlugIn {
     public void doProjection() {
 		if (imp==null)
 			return;
+		if (imp.getBitDepth()==24) {
+			doRGBProjection();
+			return;
+		}
 		sliceCount = 0;
 		if (method<AVG_METHOD || method>MEDIAN_METHOD)
 			method = AVG_METHOD;
@@ -291,13 +357,22 @@ public class ZProjector implements PlugIn {
     }
 
 	//Added by Marcel Boeglin 2013.09.23
-	/** Performs actual projection using specified method. If handleOverlay,
-	adds stack overlay elements from startSlice to stopSlice to projection*/
+	/** Performs actual projection using specified method.
+		If handleOverlay, adds stack overlay 
+		elements from startSlice to stopSlice to projection. */
 	public void doProjection(boolean handleOverlay) {
-		doProjection();
-		Overlay overlay = imp.getOverlay();
-		if (handleOverlay && overlay!=null)
-			projImage.setOverlay(projectStackRois(overlay));
+		if (isHyperstack)
+			doHyperStackProjection(allTimeFrames);
+		else if (imp.getType()==ImagePlus.COLOR_RGB)
+			doRGBProjection(handleOverlay);
+		else {
+			doProjection();
+			Overlay overlay = imp.getOverlay();
+			if (handleOverlay && overlay!=null)
+				projImage.setOverlay(projectStackRois(overlay));
+		}
+		if (projImage!=null)
+			projImage.setCalibration(imp.getCalibration());
 	}
 	
 	//Added by Marcel Boeglin 2013.09.23
diff --git a/ij/plugin/frame/ContrastAdjuster.java b/ij/plugin/frame/ContrastAdjuster.java
index c1431f4..7af15ed 100644
--- a/ij/plugin/frame/ContrastAdjuster.java
+++ b/ij/plugin/frame/ContrastAdjuster.java
@@ -820,11 +820,6 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 			return;
 		}
 		updateScrollBars(null, false);
-		//if (roi!=null) { ???
-		//	ImageProcessor mask = roi.getMask();
-		//	if (mask!=null)
-		//		ip.reset(mask);
-		//}
 		if (Recorder.record) {
 			if (Recorder.scriptMode())
 				Recorder.recordCall("IJ.run(imp, \"Enhance Contrast\", \"saturated=0.35\");");
diff --git a/ij/plugin/frame/Editor.java b/ij/plugin/frame/Editor.java
index 0c9951b..dbfd3b1 100644
--- a/ij/plugin/frame/Editor.java
+++ b/ij/plugin/frame/Editor.java
@@ -93,7 +93,7 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 
 	
 	public Editor() {
-		this(16, 60, 0, MENU_BAR);
+		this(24, 80, 0, MENU_BAR);
 	}
 
 	public Editor(int rows, int columns, int fontSize, int options) {
@@ -197,7 +197,9 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		if (window.width==0)
 			return;
 		int left = screen.width/2-window.width/2;
-		int top = (screen.height-window.height)/4;
+		int top = screen.height/(IJ.isWindows()?6:5);
+		if (IJ.isMacOSX())
+			top = (screen.height-window.height)/4;
 		if (top<0) top = 0;
 		if (nWindows<=0 || xoffset>8*XINC)
 			{xoffset=0; yoffset=0;}
@@ -1363,9 +1365,6 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			defaultDir += File.separator;
 	}
 	
-	//public void keyReleased(KeyEvent e) {}
-	//public void keyTyped(KeyEvent e) {}
-	
 	public void lostOwnership (Clipboard clip, Transferable cont) {}
 	
 	public int debug(Interpreter interp, int mode) {
diff --git a/ij/plugin/frame/LineWidthAdjuster.java b/ij/plugin/frame/LineWidthAdjuster.java
index 340969e..aadf69d 100644
--- a/ij/plugin/frame/LineWidthAdjuster.java
+++ b/ij/plugin/frame/LineWidthAdjuster.java
@@ -166,11 +166,11 @@ public class LineWidthAdjuster extends PlugInFrame implements PlugIn,
 		if (imp==null)
 			{checkbox.setState(false); return;};
 		Roi roi = imp.getRoi();
-		if (roi==null || !(roi instanceof PolygonRoi))
-			{checkbox.setState(false); return;};
-		int type = roi.getType();
-		if (type==Roi.FREEROI || type==Roi.FREELINE)
-			{checkbox.setState(false); return;};;
+		int type = roi!=null?roi.getType():null;
+		if (roi==null || !(roi instanceof PolygonRoi) || type==Roi.FREEROI || type==Roi.FREELINE || type==Roi.ANGLE) {
+			checkbox.setState(false);
+			return;
+		};
 		PolygonRoi poly = (PolygonRoi)roi;
 		boolean splineFit = poly.isSplineFit();
 		if (selected && !splineFit) {
diff --git a/ij/plugin/frame/MemoryMonitor.java b/ij/plugin/frame/MemoryMonitor.java
index 3cb6e2b..b8a3477 100644
--- a/ij/plugin/frame/MemoryMonitor.java
+++ b/ij/plugin/frame/MemoryMonitor.java
@@ -18,7 +18,7 @@ public class MemoryMonitor extends PlugInFrame {
 	private double[] mem;
 	private int index;
 	private long value;
- 	private double defaultMax = 15*1204*1024; // 15MB
+ 	private double defaultMax = 20*1024*1024; // 20MB
 	private double max = defaultMax;
 	private long maxMemory = IJ.maxMemory();
 	private boolean done;
diff --git a/ij/plugin/frame/PlugInDialog.java b/ij/plugin/frame/PlugInDialog.java
index d10b767..fbaab63 100644
--- a/ij/plugin/frame/PlugInDialog.java
+++ b/ij/plugin/frame/PlugInDialog.java
@@ -48,8 +48,8 @@ public class PlugInDialog extends Dialog implements PlugIn, WindowListener, Focu
 
 	public void windowActivated(WindowEvent e) {
 		ImageJ ij = IJ.getInstance();
-		if (IJ.isMacOSX() && ij!=null && !ij.isActive() && !(this instanceof ThresholdAdjuster))
-			ij.toFront();
+		//if (IJ.isMacOSX() && ij!=null && !ij.isActive() && !(this instanceof ThresholdAdjuster))
+		//	ij.toFront();
 		WindowManager.setWindow(this);
 	}
 
diff --git a/ij/plugin/frame/Recorder.java b/ij/plugin/frame/Recorder.java
index aab9222..743cd76 100644
--- a/ij/plugin/frame/Recorder.java
+++ b/ij/plugin/frame/Recorder.java
@@ -101,7 +101,11 @@ public class Recorder extends PlugInFrame implements PlugIn, ActionListener, Ima
 		not open or the command being recorded has called IJ.run(). 
 	*/
 	public static void setCommand(String command) {
-		boolean isMacro = Thread.currentThread().getName().startsWith("Run$_");
+		String threadName = Thread.currentThread().getName();
+		boolean isMacro = threadName.startsWith("Run$_");
+		if (threadName.contains("Popup Menu") || threadName.contains("Developer Menu"))
+			isMacro = false;
+		//IJ.log("setCommand: "+command+"  "+threadName+"  "+isMacro);
 		if (textArea==null || (isMacro&&!recordInMacros))
 			return;
 		commandName = command;
diff --git a/ij/process/AutoThresholder.java b/ij/process/AutoThresholder.java
index 395c824..55fe946 100644
--- a/ij/process/AutoThresholder.java
+++ b/ij/process/AutoThresholder.java
@@ -649,7 +649,6 @@ public class AutoThresholder {
 		}
 		// The threshold is the minimum between the two peaks.
 		for (int i=1; i<255; i++) {
-			//IJ.log(" "+i+"  "+iHisto[i]);
 			if (iHisto[i-1] > iHisto[i] && iHisto[i+1] >= iHisto[i]) {
 				threshold = i;
 				break;
diff --git a/ij/process/FHT.java b/ij/process/FHT.java
index 84277fd..8a1c96d 100644
--- a/ij/process/FHT.java
+++ b/ij/process/FHT.java
@@ -502,7 +502,7 @@ public class FHT extends FloatProcessor {
 
 
 	/** FFT imag value of one row from 2D Hartley Transform.
-	*	@author Joachim Wesner
+	*	Author: Joachim Wesner
 	*/
       void FHTimag(int row, int maxN, float[] fht, float[] imag) {
             int base = row*maxN;
diff --git a/ij/process/FloatProcessor.java b/ij/process/FloatProcessor.java
index cd0fe50..d87ad01 100644
--- a/ij/process/FloatProcessor.java
+++ b/ij/process/FloatProcessor.java
@@ -1026,6 +1026,11 @@ public class FloatProcessor extends ImageProcessor {
 		new ij.plugin.filter.Convolver().convolve(this, kernel, kernelWidth, kernelHeight);
 	}
 
+	/** Returns a 256 bin histogram of the current ROI or of the entire image if there is no ROI. */
+	public int[] getHistogram() {
+		return getStatistics().histogram;
+	}
+
 	/** Not implemented. */
 	public void threshold(int level) {}
 	/** Not implemented. */
@@ -1033,8 +1038,6 @@ public class FloatProcessor extends ImageProcessor {
 	/** Not implemented. */
 	public void medianFilter() {}
 	/** Not implemented. */
-	public int[] getHistogram() {return null;}
-	/** Not implemented. */
 	public void erode() {}
 	/** Not implemented. */
 	public void dilate() {}
diff --git a/ij/process/ImageProcessor.java b/ij/process/ImageProcessor.java
index d8caf83..584763a 100644
--- a/ij/process/ImageProcessor.java
+++ b/ij/process/ImageProcessor.java
@@ -2,6 +2,8 @@ package ij.process;
 import java.util.*;
 import java.awt.*;
 import java.awt.image.*;
+import java.awt.geom.Rectangle2D;
+import java.awt.font.GlyphVector;
 import ij.gui.*;
 import ij.util.*;
 import ij.plugin.filter.GaussianBlur;
@@ -95,6 +97,7 @@ public abstract class ImageProcessor implements Cloneable {
 	protected WritableRaster raster;
 	protected BufferedImage image;
 	protected BufferedImage fmImage;
+	protected Graphics2D fmGraphics;
 	protected ColorModel cm2;
 	protected SampleModel sampleModel;
 	protected static IndexColorModel defaultColorModel;
@@ -1266,20 +1269,27 @@ public abstract class ImageProcessor implements Cloneable {
     private ImageProcessor dotMask;
 
 	private void setupFontMetrics() {
-		if (fmImage==null)
-			fmImage=new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
 		if (fontMetrics==null) {
-			Graphics g = fmImage.getGraphics();
-			fontMetrics = g.getFontMetrics(font);
+			fmImage=new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
+			fmGraphics = (Graphics2D)fmImage.getGraphics();
+			fmGraphics.setFont(font);
+			Java2.setAntialiasedText(fmGraphics, antialiasedText);
+			fontMetrics = fmGraphics.getFontMetrics(font);
 		}
 	}
 
-	/** Draws a string at the current location using the current fill/draw value.
-        Draws multiple lines if the string contains newline characters. */
+	/** Draws a string at the current drawing location using the current fill/draw value.
+	 *  Draws multiple lines if the string contains newline characters.
+	 *  The current x coordinate is the left/center/right end of the string for
+	 *  left/center/right justification.
+	 *  The current y coordinate determines the bottommost position of the string,
+	 *  including the descent of the font (i.e., characters reaching below the baseline)
+	 *  For multi-line strings, the y coordinate applies to the first line.
+	 *  The y of the drawing position is incremented by the height of one text line,
+	 *  i.e. points at the drawing position for the next text line */
 	public void drawString(String s) {
 		if (s==null || s.equals("")) return;
 		setupFontMetrics();
-		if (ij.IJ.isMacOSX()) s += " ";
 		if (s.indexOf("\n")==-1)
 			drawString2(s);
 		else {
@@ -1289,6 +1299,8 @@ public abstract class ImageProcessor implements Cloneable {
 		}
 	}
 
+	/** Draws a single-line string at the current drawing location cx, cy and
+	 *  adds the line height (FontMetrics.getHeight) to the current y coordinate 'cy' */
 	private void drawString2(String s) {
 		int w =  getStringWidth(s);
 		int cxx = cx;
@@ -1347,7 +1359,13 @@ public abstract class ImageProcessor implements Cloneable {
 		cy += h;
 	}
 
-	/** Draws a string at the specified location using the current fill/draw value. */
+	/** Draws a string at the specified location using the current fill/draw value.
+	 *  Draws multiple lines if the string contains newline characters.
+	 *  The x coordinate is the left/center/right end of the string for left/center/right
+	 *  justification.
+	 *  The y coordinate determines the bottommost position of the string,
+	 *  including the descent of the font (i.e., characters reaching below the baseline)
+	 *  For multi-line strings, the y coordinate applies to the first line. */
 	public void drawString(String s, int x, int y) {
 		moveTo(x, y);
 		drawString(s);
@@ -1396,42 +1414,51 @@ public abstract class ImageProcessor implements Cloneable {
 	/** Sets the font used by drawString(). */
 	public void setFont(Font font) {
 		this.font = font;
-		fontMetrics = null;
 		boldFont = font.isBold();
+		setupFontMetrics();
+		fmGraphics.setFont(font);
+		Java2.setAntialiasedText(fmGraphics, antialiasedText);
+		fontMetrics = fmGraphics.getFontMetrics(font);
 	}
 	
 	/** Specifies whether or not text is drawn using antialiasing. Antialiased
 		test requires an 8 bit or RGB image. Antialiasing does not
 		work with 8-bit images that are not using 0-255 display range. */
 	public void setAntialiasedText(boolean antialiasedText) {
+		setupFontMetrics();
 		if (antialiasedText && (((this instanceof ByteProcessor)&&getMin()==0.0&&getMax()==255.0) || (this instanceof ColorProcessor)))
 			this.antialiasedText = true;
 		else
 			this.antialiasedText = false;
+		Java2.setAntialiasedText(fmGraphics, this.antialiasedText);
+		fontMetrics = fmGraphics.getFontMetrics(font);
 	}
 	
-	/** Returns the width in pixels of the specified string. */
+	/** Returns the width in pixels of the specified string, including any background
+	 *  space (whitespace) between the x drawing coordinate and the string, not necessarily
+	 *  including all whitespace at the right. */
 	public int getStringWidth(String s) {
+		// Note that fontMetrics.getStringBounds often underestimates the width (worst for italic fonts on Macs)
+		// On the other hand, GlyphVector.getPixelBounds (returned by this.getStringBounds)
+		// does not include the full character width of e.g. the '1' character, which would make
+		// lists of right-justified numbers such as the y axis of plots look ugly.
+		// Thus, the maximum of both methods is returned.
+		Rectangle2D rect = getStringBounds(s);
+		return (int)Math.max(fontMetrics.getStringBounds(s, fmGraphics).getWidth(), rect.getX()+rect.getWidth());
+	}
+
+	/** Returns a rectangle enclosing the pixels affected by drawString 
+	 *  assuming it is drawn at (x=0, y=0). As drawString draws above the drawing location,
+	 *  the y coordinate of the rectangle is negative. */
+	public Rectangle getStringBounds(String s) {
 		setupFontMetrics();
-		int w;
-		if (antialiasedText) {
-			Graphics g = fmImage.getGraphics();
-			if (g==null) {
-				fmImage = null;
-				setupFontMetrics();
-				g = fmImage.getGraphics();
-			}
-			Java2.setAntialiasedText(g, true);
-			w = Java2.getStringWidth(s, fontMetrics, g);
-			g.dispose();
-		} else
-			w =  fontMetrics.stringWidth(s);
-		return w;
+		GlyphVector gv = font.createGlyphVector(fmGraphics.getFontRenderContext(), s);
+		Rectangle2D rect = gv.getPixelBounds(null, 0.f, -fontMetrics.getDescent());
+		return new Rectangle((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
 	}
-	
+
 	/** Returns the current font. */
 	public Font getFont() {
-		setupFontMetrics();
 		return font;
 	}
 
diff --git a/ij/util/FontUtil.java b/ij/util/FontUtil.java
new file mode 100644
index 0000000..9b91158
--- /dev/null
+++ b/ij/util/FontUtil.java
@@ -0,0 +1,56 @@
+package ij.util;
+import java.text.*;
+import java.awt.*;
+
+/** This class contains static utility methods for replacing fonts that are not available on the
+ *  current system.
+ */
+
+public class FontUtil {
+
+	/** Returns a font with the given family name or, if not available, a similar font, e.g. Helvetica replaced by Arial */
+	public static Font getFont(String fontFamilyName, int style, float size) {
+		Font font = new Font(fontFamilyName, style, (int)size);
+		if (!font.getFamily().startsWith(fontFamilyName)) {
+			String[] similarFonts = getSimilarFontsList(fontFamilyName);
+			font = getFont(similarFonts, style, (int)size);
+		}
+		if (size != (int)size)
+			font = font.deriveFont(size);
+		return font;
+	}
+
+	/** Returns the font for first element of the 'fontNames' array, where a Font Family Name starts with this name.
+	 *	E.g. if fontNames = {"Times New Roman", "Serif" and the system has no "Times New Roman", but finds "Serif",
+	 *	it would return a "Serif" font with suitable style and size */
+	private static Font getFont(String[] fontNames, int style, int size) {
+		int iSize = (int)size;
+		Font font = null;
+		for (String fontName : fontNames) {
+			font = new Font(fontName, style, iSize);
+			if (font.getFamily().startsWith(fontName))
+				break;
+		}
+		return font;
+	}
+
+	/** For a few basic font types, gets a list of replacement font families
+	 *	Note that java's 'SansSerif' has wider characters (significantly different metrics) than the other
+	 *	sans-serif fonts in the list; thus it should be considered a fallback option only.
+	 *	Also note that some fonts (Times, Helvetica, Courier, Monospace, Serif) tend to truncate some
+	 *	diacritical marks when using FontMetrics.getHeight (as ImageJ does), e.g. the ring of the
+	 *  Angstrom symbol Å may be clipped (at least on Java 1.6/Mac)
+	 */
+	public static String[] getSimilarFontsList(String fontFamily) {
+		if (fontFamily.indexOf("Times")>=0 || fontFamily.indexOf("Serif")>=0)
+			return new String[]{"Times New Roman", "Times", "Liberation Serif", "Serif"};
+		else if (fontFamily.indexOf("Arial")>=0 || fontFamily.indexOf("Helvetica")>=0 || fontFamily.indexOf("Sans")>=0)
+			return new String[]{"Arial", "Helvetica", "Helvetica Neue", "Liberation Sans", "SansSerif"};
+		else if (fontFamily.indexOf("Courier")>=0 || fontFamily.indexOf("Mono")>=0)
+			return new String[]{"Courier New", "Courier", "Liberation Mono", "Monospaced"};
+		else
+			return new String[]{fontFamily};
+	}
+
+
+}
diff --git a/macros/SmoothWandTool.txt b/macros/SmoothWandTool.txt
new file mode 100644
index 0000000..4204af6
--- /dev/null
+++ b/macros/SmoothWandTool.txt
@@ -0,0 +1,104 @@
+//Smooth Wand version 1d
+//N.Vischer
+//01.07.17 20:20
+
+var         
+polyX, polyY, count;
+
+processSelection();
+exit;
+
+macro "Smooth Wand Tool - C026R0055L55ffCf00F1144" {
+    run("Select None");
+    getCursorLoc(x, y, z, flags);
+    getThreshold(lowThr, hiThr);
+    if (hiThr==-1)
+        exit("This tool only works on thresholded images");
+    doWand(x, y);
+    processSelection();
+}
+
+function processSelection() {
+   getSelectionBounds(x, y, w, h);
+    if (w<2 || h<2)
+        exit("Width and height must be > 1 px");
+    if (selectionType != 4) 
+        exit("No traced selection found");
+    getThreshold(lowThr, hiThr);
+    getSelectionCoordinates(xx, yy);
+    len = xx.length;
+    polyX = newArray(len);
+    polyY = newArray(len);
+    count = 0;
+    for(jj = 0; jj < len; jj++) {
+        x1 = xx[jj];
+        y1 = yy[jj];
+        x2 = xx[(jj+1)%len];
+        y2 = yy[(jj+1)%len];
+        dd = 1;
+        if (y1 == y2) {//horizontal separator
+            if (x1 > x2) dd = -1;
+            for(x = x1; x != x2; x+= dd){
+                processPixelPair(x, y1, x+dd, y1);
+            }
+        }
+        else {//vertical separator
+            if (y1 > y2) dd = -1;
+            for(y = y1; y != y2; y+= dd){
+                processPixelPair(x1, y, x1, y+dd);
+            }
+        }
+    }
+    polyX = Array.trim(polyX, count);
+    polyY = Array.trim(polyY, count);
+    makeSelection("polygon", polyX, polyY);
+    do{ getCursorLoc(x, y, z, flags); wait(10);}
+        while(flags&16!=0);
+    run("Interpolate", "interval=1 adjust"); //after button released
+}
+
+//processes neighbors of this separator line and adds vertex
+function processPixelPair(x1, y1, x2, y2) {
+    if (x1 == x2) {
+        val1 = getPixel(x1, minOf(y1, y2));
+        val2 = getPixel(x1 - 1, minOf(y1, y2));
+    }
+    if (y1 == y2){
+        val1 = getPixel(minOf(x1, x2), y1);
+        val2 = getPixel(minOf(x1, x2), y1 -1);
+    }
+    bright = maxOf(val1, val2);
+    dark   = minOf(val1, val2);
+
+    if (bright>=lowThr && dark<=lowThr)
+        thr = lowThr;
+    if (bright>=hiThr && dark<=hiThr)
+        thr = hiThr;
+    if (dark==bright)
+        fraction = 0.5;
+    else
+        fraction =  (thr - dark)/(bright - dark);
+    if (val1 < val2)
+        fraction = 1 - fraction;
+    if (y1 == y2) {
+        newY = minOf(y1, y2) + fraction - 0.5;
+        newX = (x1 + x2)/2;
+    }
+    if (x1 == x2) {
+        newX = minOf(x1, x2) + fraction - 0.5;
+        newY = (y1 + y2)/2;
+    }
+    polyX[count] = newX;
+    polyY[count] = newY;
+    count++;
+    if (count == polyX.length){
+        polyX = Array.concat(polyX, polyX);
+        polyY = Array.concat(polyY, polyY);
+    }       
+}
+
+
+
+
+
+
diff --git a/release-notes.html b/release-notes.html
index 32da2e0..9d158fd 100644
--- a/release-notes.html
+++ b/release-notes.html
@@ -5,24 +5,12 @@
 </head>
 <body>
 
-<li> <u>1.51p 22 June 2017</u>
+<li> <u>1.51r 23 September 2017</u>
 <ul>
-<li>  When adding a hyperstack ROI to the ROI Manager, the channel position
- is set to 0 (display in all channels) if the display mode is "Composite".
- <li> Added the setOption("ConvertToMicrons",boolean) macro function. When this
- option is set, units in TIFF images are converted to microns if the pixel width
- is less than 0.0001 cm.
- <li> Thanks to Norbert vischer, worked around a bug in Java 8 on OS X that
- sometimes caused painting of GenericDialogs to be incomplete.
- <li> Thanks to Rainer Engel, fixed a bug that caused macros that
- passed variables using "&" notation to fail if they used the
- <i>Process>Math>Macro</i> command to process a stack.
- <li> Thanks to Philippe Carl, fixed a 1.51o regression that sometimes
- caused ImageJ to freeze on Windows when opening or processing
- image stacks. 
- </ul>
- 
+<li> Fixed a 1.51q reqression that caused the Plot class to
+sometimes throw an exception.
+</ul> 
+
 <a href="http://imagej.nih.gov/ij">Home</a>
 </body>
-</html>
-
+</html>
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/imagej.git



More information about the debian-med-commit mailing list