Skip to content

Commit

Permalink
2019.10.24 (1.52r57; Plot enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
rasband committed Oct 24, 2019
1 parent 64adc75 commit 066a980
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 113 deletions.
4 changes: 2 additions & 2 deletions ij/IJ.java
Original file line number Diff line number Diff line change
Expand Up @@ -1696,8 +1696,8 @@ else if (build.length()==1)
return ImageJ.VERSION+build;
}

/** Returns the path to the home ("user.home"), startup, ImageJ, plugins, macros,
luts, temp, current or image directory if <code>title</code> is "home", "startup",
/** Returns the path to the home ("user.home"), downloads, startup, ImageJ, plugins, macros,
luts, temp, current or image directory if <code>title</code> is "home", "downloads", "startup",
"imagej", "plugins", "macros", "luts", "temp", "current", "default", "image", otherwise,
displays a dialog and returns the path to the directory selected by the user.
Returns null if the specified directory is not found or the user
Expand Down
2 changes: 1 addition & 1 deletion ij/ImageJ.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class ImageJ extends Frame implements ActionListener,

/** Plugins should call IJ.getVersion() or IJ.getFullVersion() to get the version string. */
public static final String VERSION = "1.52r";
public static final String BUILD = "54";
public static final String BUILD = "57";
public static Color backgroundColor = new Color(237,237,237);
/** SansSerif, 12-point, plain font. */
public static final Font SansSerif12 = new Font("SansSerif", Font.PLAIN, 12);
Expand Down
33 changes: 12 additions & 21 deletions ij/gui/Plot.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public class Plot implements Cloneable {
int[] enlargeRange; //whether to enlarge the range slightly to avoid values at the border (0=off, USUALLY_ENLARGE, ALWAYS_ENLARGE)
boolean logXAxis, logYAxis; //whether to really use log axis (never for small relative range)
//for passing on what should be kept when 'live' plotting (PlotMaker), but note that 'COPY_EXTRA_OBJECTS' is also on for live plotting:
int templateFlags = COPY_SIZE | COPY_LABELS | COPY_AXIS_STYLE | COPY_CONTENTS_STYLE | COPY_LEGEND;
int templateFlags = COPY_SIZE | COPY_LABELS | COPY_AXIS_STYLE | COPY_CONTENTS_STYLE | COPY_LEGEND;

Font defaultFont = DEFAULT_FONT; //default font for labels, axis, etc.
Font currentFont = defaultFont; //font as changed by setFont or setFontSize, must never be null
Expand Down Expand Up @@ -2212,9 +2212,11 @@ void zoomToRect(Rectangle r) {
* axes. Index numbers for arrows start with 0 at the 'down' arrow of the
* lower side of the x axis and end with 7 the up arrow at the upper
* side of the y axis. Numbers 8 & 9 are for "Reset Range" and "Fit All";
* numbers 10-13 for a dialog to set a single limit.
* numbers 10-13 for a dialog to set a single limit, and 14-15 for an axis options dialog.
* Numbers 10-15 must correspond to the dialogTypes as defined in PlotDialog.
*/
void zoomOnRangeArrow(int arrowIndex) {
if (arrowIndex < 0) return;
if (arrowIndex < 8) {//0..7 = arrows, 8 = Reset Range, 9 = Fit All, 10..13 = set single limit
int axisIndex = (arrowIndex / 4) * 2; //0 for x, 2 for y
double min = axisIndex == 0 ? xMin : yMin;
Expand All @@ -2236,27 +2238,16 @@ void zoomOnRangeArrow(int arrowIndex) {
}
currentMinMax[axisIndex] = min;
currentMinMax[axisIndex + 1] = max;
} else if (arrowIndex == 8)
} else if (arrowIndex == 8) {
setLimitsToDefaults(false);
else if (arrowIndex == 9)
} else if (arrowIndex == 9) {
setLimitsToFit(false);
else if (arrowIndex <= 13) {
int arrPair = arrowIndex - 10;
GenericDialog gd = new GenericDialog("Set Limit");
String[] prompts = "X-Left,X-Right,Y-Bottom,Y-Top".split(",");

gd.addNumericField(prompts[arrPair], currentMinMax[arrPair], 2);
gd.setCancelLabel("Set All Limits");
gd.showDialog();

double val = gd.getNextNumber();
currentMinMax[arrPair] = val;
defaultMinMax[arrPair] = val;
if (gd.wasCanceled()){
new PlotDialog(this, PlotDialog.SET_RANGE).showDialog(getImagePlus().getWindow());
}
} else if (arrowIndex <= 15) {
int dialogType = arrowIndex;
new PlotDialog(this, dialogType).showDialog(imp.getWindow());
}
updateImage();
if (arrowIndex <= 9) // the PlotDialog cares about updating the plot
updateImage();
}

/**
Expand Down Expand Up @@ -4030,7 +4021,7 @@ public PlotObject clone() {
}
}

/** A deep clone, which duplicates arrays etc.
/** A deep clone, which duplicates arrays etc.
* Note that colors & font are not cloned; it is assumed that these wil not be modified but replaced,
* so the clone remains unaffected */
public PlotObject deepClone() {
Expand Down
102 changes: 79 additions & 23 deletions ij/gui/PlotDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ij.process.*;
import ij.plugin.frame.Recorder;
import java.awt.*;
import java.util.Vector;

/*
* This class contains dialogs for formatting of plots (range, axes, labels, legend, creating a high-resolution plot)
Expand All @@ -11,16 +12,17 @@

public class PlotDialog implements DialogListener {

/** types of dialog */
public static final int SET_RANGE = 0, AXIS_OPTIONS = 1, LEGEND = 2, HI_RESOLUTION = 3, TEMPLATE = 4,
X_AXIS = 5, Y_AXIS = 6;
/** dialog headings for the dialogTypes */
private static final String[] HEADINGS = new String[] {"Plot Range...", "Axis Options...", "Add Legend...", "High-Resolution Plot...", "Use Template...",
"X Axis...", "Y Axis..."};
/** positions and corresponding codes for legend position */
/** Types of dialog. Note that 10-14 must be the same as the corresponding PlotWindow.rangeArrow numbers */
public static final int SET_RANGE = 0, AXIS_OPTIONS = 1, LEGEND = 2, HI_RESOLUTION = 3, TEMPLATE = 4, //5-9 spare
X_LEFT = 10, X_RIGHT = 11, Y_BOTTOM = 12, Y_TOP = 13, X_AXIS = 14, Y_AXIS = 15;
/** Dialog headings for the dialogTypes */
private static final String[] HEADINGS = new String[] {"Plot Range", "Axis Options", "Add Legend", "High-Resolution Plot", "Use Template",
null, null, null, null, null, // 5-9 spare
"X Left", "X Right", "Y Bottom","Y Top", "X Axis", "Y Axis"};
/** Positions and corresponding codes for legend position */
private static final String[] LEGEND_POSITIONS = new String[] {"Auto", "Top-Left", "Top-Right", "Bottom-Left", "Bottom-Right", "No Legend"};
private static final int[] LEGEND_POSITION_N = new int[] {Plot.AUTO_POSITION, Plot.TOP_LEFT, Plot.TOP_RIGHT, Plot.BOTTOM_LEFT, Plot.BOTTOM_RIGHT, 0};
/** template "copy what" flag: dialog texts and corresponding bit masks, in the sequence they appear in the dialog*/
/** Template "copy what" flag: dialog texts and corresponding bit masks, in the sequence as they appear in the dialog*/
private static final String[] TEMPLATE_FLAG_NAMES = new String[] {"X Range", "Y Range", "Axis Style", "Labels",
"Legend", "Contents Style", "Extra Objects (Curves...)", "Window Size"};
private static final int[] TEMPLATE_FLAGS = new int[] {Plot.X_RANGE, Plot.Y_RANGE, Plot.COPY_AXIS_STYLE, Plot.COPY_LABELS,
Expand All @@ -32,6 +34,8 @@ public class PlotDialog implements DialogListener {
private boolean dialogShowing; //when the dialog is showing, ignore the last call with event null
private Plot[] templatePlots;

private Checkbox xLogCheckbox, yLogCheckbox;

//saved dialog options: legend
private static int legendPosNumber = 0;
private static boolean bottomUp;
Expand Down Expand Up @@ -63,8 +67,10 @@ public void showDialog(Frame parent) {
if (dialogType == TEMPLATE)
plot.savePlotObjects();

GenericDialog gd = parent == null ? new GenericDialog(HEADINGS[dialogType]) :
new GenericDialog(HEADINGS[dialogType], parent);
String dialogTitle = dialogType >= X_LEFT && dialogType <= Y_TOP ?
"Set Axis Limit..." : (HEADINGS[dialogType] + "...");
GenericDialog gd = parent == null ? new GenericDialog(dialogTitle) :
new GenericDialog(dialogTitle, parent);
if (!setupDialog(gd)) return;
gd.addDialogListener(this);
dialogItemChanged(gd, null); //preview immediately
Expand Down Expand Up @@ -93,7 +99,16 @@ public void showDialog(Frame parent) {
plot.killPlotPropertiesSnapshot();
if (dialogType == TEMPLATE)
plot.killPlotObjectsSnapshot();
return;

ImagePlus imp = plot.getImagePlus();
ImageWindow win = imp == null ? null : imp.getWindow();
if (win instanceof PlotWindow)
((PlotWindow)win).hideRangeArrows(); // arrows etc might be still visible, but the mouse maybe elsewhere

if (!gd.wasCanceled() && !gd.wasOKed()) { // user has pressed "Set all limits" or "Set Axis Options" button
int newDialogType = (dialogType == SET_RANGE) ? AXIS_OPTIONS : SET_RANGE;
new PlotDialog(plot, newDialogType).showDialog(parent);
}
}

/** Setting up the dialog fields and initial parameters. The input is read in the dialogItemChanged method, which must
Expand All @@ -103,25 +118,33 @@ private boolean setupDialog(GenericDialog gd) {
double[] currentMinMax = plot.getLimits();
boolean livePlot = plot.plotMaker != null;

int xDigits = plot.logXAxis ? -2 : Plot.getDigits(currentMinMax[0], currentMinMax[1], 0.005*Math.abs(currentMinMax[1]-currentMinMax[0]), 6);
if (dialogType == SET_RANGE || dialogType == X_AXIS) {
int xDigits = plot.logXAxis ? -2 : Plot.getDigits(currentMinMax[0], currentMinMax[1], 0.005*Math.abs(currentMinMax[1]-currentMinMax[0]), 6);
gd.addNumericField("X_From", currentMinMax[0], xDigits, 6, "*");
gd.addToSameRow();
gd.addNumericField("To", currentMinMax[1], xDigits, 6, "*");
gd.setInsets(0, 20, 0); //top, left, bottom
if (livePlot)
gd.addCheckbox("Fix_X Range While Live", (plot.templateFlags & Plot.X_RANGE) != 0);
gd.addCheckbox("Log_X Axis", (plot.hasFlag(Plot.X_LOG_NUMBERS)));
gd.addCheckbox("Log_X Axis **", (plot.hasFlag(Plot.X_LOG_NUMBERS)));
xLogCheckbox = lastCheckboxAdded(gd);
enableDisableLogCheckbox(xLogCheckbox, currentMinMax[0], currentMinMax[1]);
}
int yDigits = plot.logYAxis ? -2 : Plot.getDigits(currentMinMax[2], currentMinMax[3], 0.005*Math.abs(currentMinMax[3]-currentMinMax[2]), 6);
if (dialogType == SET_RANGE || dialogType == Y_AXIS) {
int yDigits = plot.logYAxis ? -2 : Plot.getDigits(currentMinMax[2], currentMinMax[3], 0.005*Math.abs(currentMinMax[3]-currentMinMax[2]), 6);
gd.setInsets(20, 0, 3); //top, left, bottom
gd.addNumericField("Y_From", currentMinMax[2], yDigits, 6, "*");
gd.addToSameRow();
gd.addNumericField("To", currentMinMax[3], yDigits, 6, "*");
if (livePlot)
gd.addCheckbox("Fix_Y Range While Live", (plot.templateFlags & Plot.Y_RANGE) != 0);
gd.addCheckbox("Log_Y Axis", (plot.hasFlag(Plot.Y_LOG_NUMBERS)));
gd.addCheckbox("Log_Y Axis **", (plot.hasFlag(Plot.Y_LOG_NUMBERS)));
yLogCheckbox = lastCheckboxAdded(gd);
enableDisableLogCheckbox(yLogCheckbox, currentMinMax[2], currentMinMax[3]);
}
if (dialogType >= X_LEFT && dialogType <= Y_TOP) {
int digits = dialogType < Y_BOTTOM ? xDigits : yDigits;
gd.addNumericField(HEADINGS[dialogType], currentMinMax[dialogType - X_LEFT], digits, 6, "*");
}

if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS || dialogType == Y_AXIS) {
Expand Down Expand Up @@ -176,6 +199,10 @@ private boolean setupDialog(GenericDialog gd) {
gd.setInsets(10, 0, 0); //top, left, bottom
gd.addMessage("* Leave empty for automatic range");//, new Font("SansSerif", Font.PLAIN, 11));
}
if (dialogType == SET_RANGE || dialogType == X_AXIS || dialogType == Y_AXIS) {
gd.setInsets(2, 0, 0); //top, left, bottom
gd.addMessage("** Requires limits > 0 and max/min > 3");//, new Font("SansSerif", Font.PLAIN, 11));
}

if (dialogType == AXIS_OPTIONS) {
Font plotFont = (plot.currentFont != null) ? plot.currentFont : plot.defaultFont;
Expand Down Expand Up @@ -247,6 +274,13 @@ private boolean setupDialog(GenericDialog gd) {
gd.addCheckbox(TEMPLATE_FLAG_NAMES[i], flag);
}
}
// Add a button to access another dialog that might be also desired
if (dialogType >= X_LEFT && dialogType <= Y_TOP)
gd.enableYesNoCancel("OK", "Set All Limits...");
else if (dialogType == AXIS_OPTIONS)
gd.enableYesNoCancel("OK", "Set Range...");
else if (dialogType == SET_RANGE)
gd.enableYesNoCancel("OK", "Set Axis Options...");
return true;
} //setupDialog

Expand All @@ -260,11 +294,11 @@ public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
if (dialogType == SET_RANGE || dialogType == X_AXIS) {
double[] currentMinMax = plot.getLimits();
double linXMin = gd.getNextNumber();
if (gd.invalidNumber())
linXMin = Double.NaN;
//if (gd.invalidNumber())
//linXMin = Double.NaN;
double linXMax = gd.getNextNumber();
if (gd.invalidNumber())
linXMax = Double.NaN;
//if (gd.invalidNumber())
//linXMax = Double.NaN;
if (linXMin == linXMax) return false;
if (!minMaxSaved) {
plot.saveMinMax(); //save for 'Previous Range' in plot menu
Expand All @@ -275,15 +309,17 @@ public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
boolean xLog = gd.getNextBoolean();
plot.setAxisXLog(xLog);
plot.setLimitsNoUpdate(linXMin, linXMax, currentMinMax[2], currentMinMax[3]);
currentMinMax = plot.getLimits();
enableDisableLogCheckbox(xLogCheckbox, currentMinMax[0], currentMinMax[1]);
}
if (dialogType == SET_RANGE || dialogType == Y_AXIS) {
double[] currentMinMax = plot.getLimits();
double linYMin = gd.getNextNumber();
if (gd.invalidNumber())
linYMin = Double.NaN;
//if (gd.invalidNumber())
//linYMin = Double.NaN;
double linYMax = gd.getNextNumber();
if (gd.invalidNumber())
linYMax = Double.NaN;
//if (gd.invalidNumber())
//linYMax = Double.NaN;
if (linYMin == linYMax) return false;
if (!minMaxSaved) {
plot.saveMinMax(); //save for 'Previous Range' in plot menu
Expand All @@ -295,6 +331,14 @@ public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
boolean yLog = gd.getNextBoolean();
plot.setAxisYLog(yLog);
plot.setLimitsNoUpdate(currentMinMax[0], currentMinMax[1], linYMin, linYMax);
currentMinMax = plot.getLimits();
enableDisableLogCheckbox(yLogCheckbox, currentMinMax[2], currentMinMax[3]);
}
if (dialogType >= X_LEFT && dialogType <= Y_TOP) {
double newLimit = gd.getNextNumber();
double[] minMaxCopy = (double[])(plot.getLimits().clone());
minMaxCopy[dialogType - X_LEFT] = newLimit;
plot.setLimitsNoUpdate(minMaxCopy[0], minMaxCopy[1], minMaxCopy[2], minMaxCopy[3]);
}

if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS || dialogType == Y_AXIS) {
Expand Down Expand Up @@ -462,6 +506,13 @@ private void doHighResolutionDialog(Frame parent) {
}
}

/** Disables switching on a checkbox for log range if the axis limits do not allow it.
* The checkbox can be always switched off. */
void enableDisableLogCheckbox(Checkbox checkbox, double limit1, double limit2) {
boolean logPossible = limit1 > 0 && limit2 > 0 && (limit1 > 3*limit2 || limit2 > 3*limit1);
checkbox.setEnabled(logPossible);
}


boolean getFlag(int flags, int bitMask) {
return (flags&bitMask) != 0;
Expand All @@ -473,5 +524,10 @@ int setFlag(int flags, int bitMask, boolean state) {
return flags;
}

Checkbox lastCheckboxAdded(GenericDialog gd) {
Vector checkboxes = gd.getCheckboxes();
return (Checkbox)(checkboxes.get(checkboxes.size() - 1));
}

}

Loading

0 comments on commit 066a980

Please sign in to comment.