[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