package ij.gui;
import ij.*;
import ij.process.*;
import ij.measure.CurveFitter;
import ij.measure.Minimizer;
import ij.text.TextWindow;
import ij.measure.ResultsTable;
import ij.plugin.Colors;
import ij.plugin.frame.Recorder;
import ij.util.Tools;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import java.util.Vector;
import java.util.ArrayList;
public class PlotContentsDialog implements DialogListener {
public final static int ERROR=-1, STYLE=0, ADD_FROM_PLOT=1, ADD_FROM_TABLE=2, ADD_FROM_ARRAYS=3, ADD_FIT=4;
private static final String[] HEADINGS = new String[] {"Plot Contents Style", "Add From Plot", "Plot From Table", "Add Plot Data", "Add Fit"};
private Plot plot; private int dialogType; private double[] savedLimits; GenericDialog gd;
private int currentObjectIndex = -1;
private Choice objectChoice;
private Choice symbolChoice;
private TextField colorField, color2Field, labelField, widthField;
private Checkbox visibleCheckbox;
private boolean creatingPlot; private Choice plotChoice; private Plot[] allPlots;
private String[] allPlotNames;
private static Plot previousPlot;
private static int previousPlotObjectIndex;
private int defaultPlotIndex, defaultObjectIndex;
private int currentPlotNumObjects;
private Choice tableChoice; final static int N_COLUMNS = 4; private int nColumnsToUse = N_COLUMNS; private Choice[] columnChoice = new Choice[N_COLUMNS];
private final static String[] COLUMN_NAMES = new String[] {"X:", "Y:", "X Error:", "Y Error:"};
private final static boolean[] COLUMN_ALLOW_NONE = new boolean[] {true, false, true, true}; private ResultsTable[] allTables;
private String[] allTableNames;
private static String previousTableName;
private static int[] previousColumns = new int[]{1, 1, 0, 0}; private static int defaultTableIndex;
private static int[] defaultColumnIndex = new int[N_COLUMNS];
private String[] arrayHeadings; private ArrayList<float[]> arrayData;
private Choice fitDataChoice; private Choice fitFunctionChoice;
private Thread fittingThread;
private static String lastFitFunction = CurveFitter.fitList[0];
private String curveFitterStatusString;
private static String previousColor="blue", previousColor2="#a0a0ff", previousSymbol="Circle";
private static double previousLineWidth = 1;
private static final String[] PLOT_COLORS = new String[] {"blue", "red", "black", "#00c0ff", "#00e000", "gray", "#c08060", "magenta"};
public PlotContentsDialog(Plot plot, int dialogType) {
this.plot = plot;
this.dialogType = plot == null ? ERROR : dialogType;
if (plot != null) currentPlotNumObjects = plot.getNumPlotObjects();
}
public PlotContentsDialog(String title, ResultsTable rt) {
creatingPlot = true;
dialogType = ADD_FROM_TABLE;
if (rt == null || !isValid(rt)) {
IJ.error("Cant Create Plot","No (results) table or no data in "+title);
dialogType = ERROR;
}
plot = new Plot("Plot of "+title, "x", "y");
allTables = new ResultsTable[] {rt};
allTableNames = new String[] {title};
}
public PlotContentsDialog(String title, String[] headings, String[] defaultHeadings, ArrayList<float[]> data) {
creatingPlot = true;
dialogType = ADD_FROM_ARRAYS;
this.arrayHeadings = headings;
this.arrayData = data;
plot = new Plot(title, "x", "y");
setDefaultColumns(defaultHeadings);
}
public PlotContentsDialog(Plot plot, String[] headings, String[] defaultHeadings, ArrayList<float[]> data) {
dialogType = ADD_FROM_ARRAYS;
this.plot = plot;
this.arrayHeadings = headings;
this.arrayData = data;
setDefaultColumns(defaultHeadings);
}
public void noErrorBars() {
nColumnsToUse = 2; }
public void showDialog(Frame parent) {
if (dialogType == ERROR) return;
if (!creatingPlot) savedLimits = plot.getLimits();
plot.savePlotObjects();
String[] designations = plot.getPlotObjectDesignations();
if (dialogType == STYLE && designations.length==0) {
IJ.error("Empty Plot");
return;
} else if (dialogType == ADD_FROM_PLOT) {
prepareAddFromPlot();
if (allPlots.length == 0) return; } else if (dialogType == ADD_FROM_TABLE && !creatingPlot) {
prepareAddFromTable();
if (allTables.length == 0) return; }
if ((dialogType == ADD_FROM_TABLE || dialogType == ADD_FROM_ARRAYS) && !creatingPlot)
suggestColor();
if (parent == null && plot.getImagePlus() != null)
parent = plot.getImagePlus().getWindow();
gd = parent == null ? new GenericDialog(HEADINGS[dialogType]) :
new GenericDialog(HEADINGS[dialogType], parent);
IJ.wait(100); if (dialogType == STYLE) {
gd.addChoice("Item:", designations, designations[0]);
objectChoice = (Choice)(gd.getChoices().get(0));
currentObjectIndex = objectChoice.getSelectedIndex();
} else if (dialogType == ADD_FROM_PLOT) {
gd.addChoice("Select Plot:", allPlotNames, allPlotNames[defaultPlotIndex]);
gd.addChoice("Item to Add:", new String[]{""}, ""); Vector choices = gd.getChoices();
plotChoice = (Choice)(choices.get(0));
objectChoice = (Choice)(choices.get(1));
makeSourcePlotObjects();
} else if (dialogType == ADD_FROM_TABLE) {
gd.addChoice("Select Table:", allTableNames, allTableNames[defaultTableIndex]);
tableChoice = (Choice)(gd.getChoices().get(0));
if (creatingPlot) tableChoice.setEnabled(false); } else if (dialogType == ADD_FIT) {
String[] dataSources = plot.getDataObjectDesignations();
if (dataSources.length == 0) {
IJ.error("No Data For Fitting");
return;
}
gd.addChoice("Fit Data Set:", dataSources, dataSources[0]);
gd.addChoice("Fit Function:", new String[0], "");
Vector choices = gd.getChoices();
fitDataChoice = (Choice)(choices.get(0));
fitFunctionChoice = (Choice)(choices.get(1));
if (dataSources.length == 1)
fitDataChoice.setEnabled(false);
for (int i=0; i<CurveFitter.fitList.length; i++)
fitFunctionChoice.addItem(CurveFitter.fitList[CurveFitter.sortedTypes[i]]);
fitFunctionChoice.select(lastFitFunction);
}
if (dialogType == ADD_FROM_TABLE || dialogType == ADD_FROM_ARRAYS) {
for (int i=0; i<nColumnsToUse; i++) {
gd.addChoice(COLUMN_NAMES[i], new String[]{""}, ""); Vector choices = gd.getChoices();
columnChoice[i] = (Choice)(choices.get(choices.size()-1));
}
makeSourceColumns();
if (!creatingPlot)
suggestNewYColumn();
}
gd.addStringField("Color:", previousColor, 10);
gd.addStringField("Secondary (fill) color:", previousColor2, 10);
gd.addNumericField("Line width: ", previousLineWidth, 1);
gd.addChoice("Symbol:", Plot.SORTED_SHAPES, dialogType == ADD_FIT ? "line" : previousSymbol);
gd.addStringField("Label:", "", 20);
gd.setInsets(10, 60, 0);
gd.addCheckbox("Visible", true);
Vector choices = gd.getChoices();
symbolChoice = (Choice)(choices.get(choices.size()-1));
Vector stringFields = gd.getStringFields();
colorField = (TextField)(stringFields.get(0));
color2Field = (TextField)(stringFields.get(1));
labelField = (TextField)(stringFields.get(2));
widthField = (TextField)(gd.getNumericFields().get(0));
visibleCheckbox = (Checkbox)(gd.getCheckboxes().get(0));
gd.addDialogListener(this);
IJ.wait(100); if (dialogType == STYLE)
setDialogStyleFields(objectChoice.getSelectedIndex());
else if (dialogType == ADD_FROM_PLOT)
addObjectFromPlot();
else if (dialogType == ADD_FROM_TABLE || dialogType == ADD_FROM_ARRAYS)
addObjectFromTable();
else if (dialogType == ADD_FIT)
addFitCurve();
plot.updateImage();
if (creatingPlot) {
boolean recording = Recorder.record;
Recorder.record = false; plot.show();
if (recording) Recorder.record = true;
}
gd.showDialog();
if (fittingThread != null) {
fittingThread.interrupt();
try {
fittingThread.join();
} catch (InterruptedException e) {}
}
if (gd.wasCanceled()) {
if (creatingPlot) {
ImagePlus imp = plot.getImagePlus();
if (imp != null) imp.close();
} else {
plot.restorePlotObjects();
if (savedLimits != null)
plot.setLimits(savedLimits[0], savedLimits[1], savedLimits[2], savedLimits[3]);
plot.updateImage();
plot.killPlotObjectsSnapshot();
}
return;
}
plot.killPlotObjectsSnapshot();
if (dialogType == ADD_FROM_TABLE || dialogType == ADD_FROM_ARRAYS || dialogType == ADD_FIT) {
previousColor = colorField.getText();
previousColor2 = color2Field.getText();
previousSymbol = symbolChoice.getSelectedItem();
previousLineWidth = Tools.parseDouble(widthField.getText());
}
if (dialogType == ADD_FIT) {
lastFitFunction = fitFunctionChoice.getSelectedItem();
IJ.log(curveFitterStatusString);
}
if (Recorder.record && !Recorder.scriptMode()) {
if (dialogType == ADD_FROM_PLOT) {
Recorder.recordString("Plot.addFromPlot(\""+plotChoice.getSelectedItem()+"\", "+objectChoice.getSelectedIndex()+");\n");
} else if (dialogType == ADD_FROM_TABLE) {
if (creatingPlot)
Recorder.recordString("Plot.create(\""+plot.getTitle()+"\", \""+plot.getLabel('x')+"\", \""+plot.getLabel('y')+"\");\n");
String xSource = columnChoice[0].getSelectedIndex() == 0 ? "" :
"Table.getColumn(\""+columnChoice[0].getSelectedItem()+"\", \""+tableChoice.getSelectedItem()+"\"), ";
String ySource = "Table.getColumn(\""+columnChoice[1].getSelectedItem()+"\", \""+tableChoice.getSelectedItem()+"\")";
Recorder.recordString("Plot.add(\""+symbolChoice.getSelectedItem()+"\", "+xSource+ySource+");\n");
if (columnChoice[2].getSelectedIndex() > 0)
Recorder.recordString("Plot.add(\"xerror\", Table.getColumn(\""+
columnChoice[2].getSelectedItem()+"\", \""+tableChoice.getSelectedItem()+"\"));\n");
if (columnChoice[3].getSelectedIndex() > 0)
Recorder.recordString("Plot.add(\"error\", Table.getColumn(\""+
columnChoice[3].getSelectedItem()+"\", \""+tableChoice.getSelectedItem()+"\"));\n");
}
Recorder.recordString("Plot.setStyle("+currentObjectIndex+", \""+getStyleString()+"\");\n");
}
}
public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
if (e==null)
return true; boolean setStyle = false;
if (dialogType == STYLE) {
int objectIndex = objectChoice.getSelectedIndex(); setStyle = (e.getSource() != objectChoice);
if (e.getSource() == objectChoice) {
setDialogStyleFields(objectIndex);
currentObjectIndex = objectIndex;
} else
setStyle = true;
} else if (dialogType == ADD_FROM_PLOT) {
if (e.getSource() == plotChoice) {
makeSourcePlotObjects();
addObjectFromPlot();
} else if (e.getSource() == objectChoice) {
addObjectFromPlot();
} else
setStyle = true;
} else if (dialogType == ADD_FROM_TABLE || dialogType == ADD_FROM_ARRAYS) {
if (e.getSource() == tableChoice) {
makeSourceColumns();
addObjectFromTable();
} else {
boolean columnChanged = false;
for (int c=0; c<nColumnsToUse; c++)
if (e.getSource() == columnChoice[c]) {
columnChanged = true;
break;
}
if (columnChanged)
addObjectFromTable();
else
setStyle = true;
}
} else if (dialogType == ADD_FIT) {
if (e.getSource() == fitDataChoice || e.getSource() == fitFunctionChoice)
addFitCurve();
else
setStyle = true;
}
if (setStyle)
setPlotObjectStyle();
return true;
}
private void setPlotObjectStyle() {
if (currentObjectIndex < 0) return;
String style = getStyleString();
plot.setPlotObjectStyle(currentObjectIndex, style);
if (labelField.isEnabled()) {
String label = labelField.getText();
plot.setPlotObjectLabel(currentObjectIndex, label.length() > 0 ? label : null);
}
}
private String getStyleString() {
String color = colorField.getText();
String color2 = color2Field.getText();
double width = Tools.parseDouble(widthField.getText());
String symbol = symbolChoice.getSelectedItem();
Boolean visible = visibleCheckbox.getState();
String style = color.trim()+","+color2.trim()+","+(float)width+","+ symbol+(visible?"":"hidden");
return style;
}
private void setDialogStyleFields(int index) {
if (index < 0) return;
Checkbox visibleC = (Checkbox)gd.getCheckboxes().get(0);
String styleString = plot.getPlotObjectStyle(index);
String designation = plot.getPlotObjectDesignations()[index].toLowerCase();
boolean isData = designation.startsWith("data");
boolean isText = designation.startsWith("text");
boolean isBox = designation.startsWith("shapes") &&
(designation.contains("boxes") || designation.contains("rectangles"));
boolean isGrid = designation.startsWith("shapes") && designation.contains("redraw_grid");
String[] items = styleString.split(",");
colorField.setText(items[0]);
color2Field.setText(items[1]);
widthField.setText(items[2]);
if (items.length >= 4)
symbolChoice.select(items[3]);
labelField.setText(isData ? plot.getPlotObjectLabel(index) : "");
visibleC.setState(!styleString.contains("hidden"));
colorField.setEnabled(!isGrid); color2Field.setEnabled(isData || isBox); widthField.setEnabled(!isText && !isGrid); symbolChoice.setEnabled(isData); labelField.setEnabled(isData); }
private void prepareAddFromPlot() {
int[] windowIDlist = WindowManager.getIDList();
ArrayList<ImagePlus> plotImps = new ArrayList<ImagePlus>();
ImagePlus currentPlotImp = null;
for (int windowID : windowIDlist) {
ImagePlus imp = WindowManager.getImage(windowID);
if (imp == null || imp.getWindow() == null) continue;
Plot thePlot = (Plot)(imp.getProperty(Plot.PROPERTY_KEY));
if (thePlot != null) {
if (thePlot == plot)
currentPlotImp = imp;
else
plotImps.add(imp);
}
}
if (currentPlotImp != null)
plotImps.add(currentPlotImp); if (plotImps.size() == 0) return;
allPlots = new Plot[plotImps.size()];
allPlotNames = new String[plotImps.size()];
defaultPlotIndex = 0;
for (int i=0; i<allPlots.length; i++) {
ImagePlus imp = plotImps.get(i);
allPlots[i] = (Plot)(imp.getProperty(Plot.PROPERTY_KEY));
if (allPlots[i] == previousPlot)
defaultPlotIndex = i;
allPlotNames[i] = imp.getWindow().getTitle();
if (imp == currentPlotImp)
allPlotNames[i] = "THIS PLOT: " + allPlotNames[i];
}
}
private void makeSourcePlotObjects() {
int plotIndex = plotChoice.getSelectedIndex();
String[] plotObjectNames = allPlots[plotIndex].getPlotObjectDesignations();
objectChoice.removeAll();
int nPlotObjects = allPlots[plotIndex] == plot ? currentPlotNumObjects : plotObjectNames.length; for (int i=0; i<nPlotObjects; i++)
objectChoice.addItem(plotObjectNames[i]);
objectChoice.select(Math.min(nPlotObjects-1, previousPlotObjectIndex));
}
private void addObjectFromPlot() {
int plotIndex = plotChoice.getSelectedIndex();
int objectIndex = objectChoice.getSelectedIndex();
plot.restorePlotObjects();
if (savedLimits != null)
plot.setLimits(savedLimits);
currentObjectIndex = plot.addObjectFromPlot(allPlots[plotIndex], objectIndex); plot.fitRangeToLastPlotObject();
setDialogStyleFields(currentObjectIndex);
previousPlot = allPlots[plotIndex];
previousPlotObjectIndex = objectIndex;
}
private void prepareAddFromTable() {
ArrayList<TextWindow> tableWindows = new ArrayList<TextWindow>();
Frame[] windows = WindowManager.getNonImageWindows();
for (Frame win : windows) {
if (!(win instanceof TextWindow)) continue;
ResultsTable rt = ((TextWindow)win).getResultsTable();
if (isValid(rt))
tableWindows.add((TextWindow)win);
}
allTables = new ResultsTable[tableWindows.size()];
allTableNames = new String[tableWindows.size()];
defaultTableIndex = 0;
for (int i=0; i<allTables.length; i++) {
TextWindow tw = tableWindows.get(i);
allTables[i] = tw.getResultsTable();
if (allTableNames[i] == previousTableName)
defaultTableIndex = i;
allTableNames[i] = tw.getTitle();
}
}
public static boolean tableWindowExists() {
Frame[] windows = WindowManager.getNonImageWindows();
for (Frame win : windows) {
if (win instanceof TextWindow) {
ResultsTable rt = ((TextWindow)win).getResultsTable();
if (isValid(rt)) return true;
}
}
return false;
}
private void makeSourceColumns() {
String[] columnHeadings = null;
if (dialogType == ADD_FROM_TABLE) {
int tableIndex = tableChoice.getSelectedIndex();
ResultsTable rt = allTables[tableIndex];
String columnHeadingStr = rt.getColumnHeadings();
if (!columnHeadingStr.startsWith(" \t"))
columnHeadingStr = " \t"+columnHeadingStr; columnHeadings = columnHeadingStr.split("\t");
int nBadColumns = 0;
for (int i=1; i<columnHeadings.length; i++) { int index = rt.getColumnIndex(columnHeadings[i]);
if (!rt.columnExists(index)) {
columnHeadings[i] = null;
nBadColumns++;
}
}
if (nBadColumns > 0) {
String[] newHeadings = new String[columnHeadings.length - nBadColumns];
int i=0;
for (String heading : columnHeadings) if (heading != null)
newHeadings[i++] = heading;
columnHeadings = newHeadings;
}
} else { columnHeadings = new String[arrayHeadings.length+1];
System.arraycopy(arrayHeadings, 0, columnHeadings, 1, arrayHeadings.length);
}
columnHeadings[0] = "---";
for (int c=0; c<nColumnsToUse; c++) {
columnChoice[c].removeAll();
for (int i=COLUMN_ALLOW_NONE[c] ? 0 : 1; i<columnHeadings.length; i++)
columnChoice[c].addItem(columnHeadings[i]);
if (columnChoice[c].getItemCount() > 0 && previousColumns[c] >= 0)
columnChoice[c].select(Math.min(columnChoice[c].getItemCount()-1, previousColumns[c]));
}
}
private void addObjectFromTable() {
float[][] data = getDataArrays();
if (data[1] == null) return; String label = columnChoice[1].getSelectedItem(); int shape = Plot.toShape(symbolChoice.getSelectedItem());
float lineWidth = (float)(Tools.parseDouble(widthField.getText()));
if (lineWidth > 0)
plot.setLineWidth(lineWidth);
plot.restorePlotObjects();
if (savedLimits != null)
plot.setLimits(savedLimits);
plot.setColor(colorField.getText(), color2Field.getText());
plot.addPoints(data[0], data[1], data[3], shape, label);
if (data[2] != null)
plot.addHorizontalErrorBars(data[2]);
if (creatingPlot) {
plot.setXYLabels(data[0]==null ? "x" : columnChoice[0].getSelectedItem(), columnChoice[1].getSelectedItem());
plot.setLimitsToFit(false);
} else
plot.fitRangeToLastPlotObject();
currentObjectIndex = plot.getNumPlotObjects()-1;
setDialogStyleFields(currentObjectIndex);
if (dialogType == ADD_FROM_TABLE)
previousTableName = allTableNames[tableChoice.getSelectedIndex()];
}
private void suggestNewYColumn() {
int nYcolumns = columnChoice[1].getItemCount();
int currentIndex = columnChoice[1].getSelectedIndex();
for (int i=0; i<nYcolumns; i++) {
if (columnChoice[0].getSelectedIndex()==0 || !columnChoice[1].getSelectedItem().equals(columnChoice[0].getSelectedItem())) {
float[][] data = getDataArrays();
data = new float[][] {data[0], data[1]}; if (plot.getPlotObjectIndex(data) < 0)
return; }
columnChoice[1].select((currentIndex+i+1)%nYcolumns); }
}
private float[][] getDataArrays() {
float[][] data = new float[N_COLUMNS][];
if (dialogType == ADD_FROM_TABLE) {
ResultsTable rt = allTables[tableChoice.getSelectedIndex()];
for (int c=0; c<N_COLUMNS; c++) {
String heading = columnChoice[c].getSelectedItem();
int index = rt.getColumnIndex(heading);
if (index >= 0)
data[c] = rt.getColumn(index);
previousColumns[c] = columnChoice[c].getSelectedIndex();
}
} else { for (int c=0; c<nColumnsToUse; c++) {
String heading = columnChoice[c].getSelectedItem();
int index = getIndex(arrayHeadings, heading);
if (index >= 0)
data[c] = arrayData.get(index);
previousColumns[c] = columnChoice[c].getSelectedIndex();
}
}
return data;
}
private void addFitCurve() {
plot.restorePlotObjects();
if (savedLimits != null)
plot.setLimits(savedLimits);
int dataIndex = fitDataChoice.getSelectedIndex();
float[][] data = plot.getDataObjectArrays(dataIndex);
String fitName = fitFunctionChoice.getSelectedItem();
int fitType = getIndex(CurveFitter.fitList, fitName);
CurveFitter cf = new CurveFitter(Tools.toDouble(data[0]), Tools.toDouble(data[1]));
cf.doFit(fitType);
String statusString = "Fit: "+Minimizer.STATUS_STRING[cf.getStatus()];
if (cf.getStatus() == Minimizer.SUCCESS)
statusString += ", sum residuals ^2 = "+(float)(cf.getSumResidualsSqr());
IJ.showStatus(statusString);
curveFitterStatusString = "Fit for "+plot.getTitle()+": "+fitDataChoice.getSelectedItem()+cf.getResultString();
double[] plotMinMax = plot.getLimits();
double[] dataMinMax = Tools.getMinMax(data[0]);
double min = Math.min(plotMinMax[0], dataMinMax[0]);
double max = Math.max(plotMinMax[1], dataMinMax[1]);
double plotSpan = Math.abs(plotMinMax[1] - plotMinMax[0]);
double dataSpan = Math.abs(dataMinMax[1] - dataMinMax[0]);
double rangeFactor = Math.max(plotSpan/dataSpan, dataSpan/plotSpan);
if (rangeFactor > 20) rangeFactor = 20;
int nPoints = (int)(1000*rangeFactor); float[] xFit = new float[nPoints];
float[] yFit = new float[nPoints];
for (int i=0; i<nPoints; i++) {
xFit[i] = (float)(min + i*((max-min)/(nPoints-1)));
yFit[i] = (float)(cf.f(xFit[i]));
}
plot.addPoints(xFit, yFit, Plot.LINE);
currentObjectIndex = plot.getNumPlotObjects()-1;
labelField.setText("Fit: "+fitName);
setPlotObjectStyle();
}
private static boolean isValid(ResultsTable rt) {
if (rt == null) return false;
String columnHeadingStr = rt.getColumnHeadings();
if (columnHeadingStr.startsWith(" \t"))
return columnHeadingStr.length() >=3;
else
return columnHeadingStr.length() >= 1;
}
private void setDefaultColumns(String[] defaultHeadings) {
if (defaultHeadings == null) return;
for (int i=0; i<Math.min(defaultHeadings.length, nColumnsToUse); i++) {
if (defaultHeadings[i] == null || defaultHeadings[i].length()==0)
previousColumns[i] = 0;
else
previousColumns[i] = Math.max(0, getIndex(arrayHeadings, defaultHeadings[i]) + (COLUMN_ALLOW_NONE[i] ? 1 : 0));
}
}
private static int getIndex(String[] strArray, String searchTerm) {
for (int i=0; i<strArray.length; i++)
if (strArray[i].equals(searchTerm)) return i;
return -1;
}
private void suggestColor() {
boolean[] colorUsed = new boolean[PLOT_COLORS.length];
String[] designations = plot.getPlotObjectDesignations();
for (int i=0; i<designations.length; i++) {
if (!(designations[i].toLowerCase().startsWith("data"))) continue;
String styleString = plot.getPlotObjectStyle(i);
for (int j=0; j<PLOT_COLORS.length; j++)
if (styleString.startsWith(PLOT_COLORS[j]))
colorUsed[j] = true;
}
String newColor = previousColor;
for (int j=0; j<PLOT_COLORS.length; j++)
if (!colorUsed[j]) {
newColor = PLOT_COLORS[j];
break;
}
if (previousColor2 != null && previousColor2.equals(previousColor)) previousColor2 = newColor;
else if (Colors.decode(previousColor2, null) != null) { Color newC = Colors.decode(newColor,Color.BLACK);
Color newC2 = new Color(makeBrighter(newC.getRed()), makeBrighter(newC.getGreen()), makeBrighter(newC.getBlue()));
previousColor2 = Colors.colorToString(newC2);
}
previousColor = newColor;
}
private static int makeBrighter(int v) {
return v> 190 ? 255 : 255 - (int)(0.4*(255-v));
}
}