package cz.cuni.lf1.lge.ThunderSTORM;
import cz.cuni.lf1.lge.ThunderSTORM.UI.AnalysisOptionsDialog;
import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params.LABEL_X;
import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params.LABEL_Y;
import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params.LABEL_Z;
import cz.cuni.lf1.lge.ThunderSTORM.UI.CardsPanel;
import cz.cuni.lf1.lge.ThunderSTORM.results.IJResultsTable;
import cz.cuni.lf1.lge.ThunderSTORM.UI.GUI;
import cz.cuni.lf1.lge.ThunderSTORM.UI.MacroParser;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.MoleculeDescriptor;
import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.MoleculeDescriptor.Units.PIXEL;
import static cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.MoleculeDescriptor.Units.NANOMETER;
import cz.cuni.lf1.lge.ThunderSTORM.rendering.IncrementalRenderingMethod;
import cz.cuni.lf1.lge.ThunderSTORM.rendering.RenderingQueue;
import cz.cuni.lf1.lge.ThunderSTORM.rendering.ui.AbstractRenderingUI;
import cz.cuni.lf1.lge.ThunderSTORM.rendering.ui.EmptyRendererUI;
import cz.cuni.lf1.lge.ThunderSTORM.rendering.ui.IRendererUI;
import cz.cuni.lf1.lge.ThunderSTORM.results.GenericTable;
import cz.cuni.lf1.lge.ThunderSTORM.results.IJGroundTruthTable;
import cz.cuni.lf1.lge.ThunderSTORM.util.GridBagHelper;
import cz.cuni.lf1.lge.ThunderSTORM.util.VectorMath;
import ij.IJ;
import ij.ImagePlus;
import ij.Macro;
import ij.Prefs;
import ij.plugin.PlugIn;
import ij.plugin.frame.Recorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
public class RenderingPlugIn implements PlugIn {
private enum AutoSize {
MANUAL("manual"),
RESULTS("results"),
IMAGE("image");
private String value;
private AutoSize(String val) {
this.value = val;
}
public String getValue() {
return this.value;
}
};
public static Rectangle resizeByResults(double [] xpos, double [] ypos, double magnification) {
int left = ((int)Math.max(VectorMath.min(xpos) * magnification, 0));
int top = ((int)Math.max(VectorMath.min(ypos) * magnification, 0));
int sizeX = (int)(VectorMath.max(xpos) * magnification) - left + 1;
int sizeY = (int)(VectorMath.max(ypos) * magnification) - top + 1;
int mag = (int)magnification;
return new Rectangle(left/mag, top/mag, sizeX/mag, sizeY/mag);
}
@Override
public void run(String string) {
GUI.setLookAndFeel();
//
boolean preview = false;
GenericTable table;
if(IJGroundTruthTable.IDENTIFIER.equals(string)) {
if(!IJGroundTruthTable.isGroundTruthWindow()) {
IJ.error("Requires `" + IJGroundTruthTable.IDENTIFIER + "` window open!");
return;
}
table = IJGroundTruthTable.getGroundTruthTable();
} else {
if(!IJResultsTable.isResultsWindow()) {
IJ.error("Requires `" + IJResultsTable.IDENTIFIER + "` window open!");
return;
}
table = IJResultsTable.getResultsTable();
preview = true;
}
//
if(!table.columnExists(LABEL_X) || !table.columnExists(LABEL_Y)) {
IJ.error(String.format("X and Y columns not found in Results table. Looking for: %s and %s. Found: %s.", LABEL_X, LABEL_Y, table.getColumnNames()));
return;
}
double[] xpos = table.getColumnAsDoubles(LABEL_X, MoleculeDescriptor.Units.PIXEL);
double[] ypos = table.getColumnAsDoubles(LABEL_Y, MoleculeDescriptor.Units.PIXEL);
if(xpos == null || ypos == null) {
IJ.error("results were null");
return;
}
double[] z = table.columnExists(LABEL_Z) ? table.getColumnAsDoubles(LABEL_Z) : null;
double[] dx = null;
if(table.columnExists(MoleculeDescriptor.Fitting.LABEL_UNCERTAINTY_XY)) {
dx = table.getColumnAsDoubles(MoleculeDescriptor.Fitting.LABEL_UNCERTAINTY_XY, MoleculeDescriptor.Units.PIXEL);
}
double[] dz = null;
if(table.columnExists(MoleculeDescriptor.Fitting.LABEL_UNCERTAINTY_Z)) {
dz = table.getColumnAsDoubles(MoleculeDescriptor.Fitting.LABEL_UNCERTAINTY_Z, MoleculeDescriptor.Units.NANOMETER);
}
List<IRendererUI> knownRenderers = ModuleLoader.getUIModules(IRendererUI.class);
//do not show EmptyRenderer
for(Iterator<IRendererUI> it = knownRenderers.iterator(); it.hasNext();) {
IRendererUI rendererUI = it.next();
if(rendererUI instanceof EmptyRendererUI) {
it.remove();
} else if(rendererUI instanceof AbstractRenderingUI) {
((AbstractRenderingUI) rendererUI).setShowRepaintFrequency(false);
}
}
IRendererUI selectedRendererUI;
double sizeX, sizeY, left, top;
boolean setAsPreview = false;
ImagePlus im;
if(MacroParser.isRanFromMacro()) {
MacroParser parser = new MacroParser(false, null, null, null, knownRenderers);
selectedRendererUI = parser.getRendererUI();
String autosize = Macro.getValue(Macro.getOptions(), "autosize", AutoSize.MANUAL.getValue());
if (autosize.equals(AutoSize.IMAGE.getValue()) && (IJResultsTable.IDENTIFIER.equals(table.getTableIdentifier()) && (im = ((IJResultsTable) table).getAnalyzedImage()) != null)) {
left = 0;
top = 0;
sizeX = im.getWidth();
sizeY = im.getHeight();
} else if (autosize.equals(AutoSize.RESULTS.getValue())) {
Rectangle rect = resizeByResults(xpos, ypos, new EmptyRendererUI().magnification.getValue());
left = rect.getX();
top = rect.getY();
sizeX = rect.getWidth();
sizeY = rect.getHeight();
} else { // MANUAL
left = Double.parseDouble(Macro.getValue(Macro.getOptions(), "imleft", "0"));
top = Double.parseDouble(Macro.getValue(Macro.getOptions(), "imtop", "0"));
sizeX = Double.parseDouble(Macro.getValue(Macro.getOptions(), "imwidth", "0"));
sizeY = Double.parseDouble(Macro.getValue(Macro.getOptions(), "imheight", "0"));
}
} else {
double guessedLeft, guessedTop, guessedWidth, guessedHeight;
if(IJResultsTable.IDENTIFIER.equals(table.getTableIdentifier()) && (im = ((IJResultsTable) table).getAnalyzedImage()) != null) {
guessedLeft = 0;
guessedTop = 0;
guessedWidth = im.getWidth();
guessedHeight = im.getHeight();
} else {
Rectangle rect = resizeByResults(xpos, ypos, new EmptyRendererUI().magnification.getValue());
guessedLeft = rect.getX();
guessedTop = rect.getY();
guessedWidth = rect.getWidth();
guessedHeight = rect.getHeight();
}
RenderingDialog dialog = new RenderingDialog(preview, knownRenderers, guessedLeft, guessedTop, guessedWidth, guessedHeight);
dialog.setVisible(true);
if(dialog.result == RenderingDialog.DialogResult.CANCELLED) {
return;
}
if(dialog.result == RenderingDialog.DialogResult.PREVIEW) {
setAsPreview = true;
}
selectedRendererUI = dialog.getSelectedRendererUI();
left = dialog.left;
top = dialog.top;
sizeX = dialog.sizeX;
sizeY = dialog.sizeY;
}
selectedRendererUI.setSize(left, top, sizeX, sizeY);
IncrementalRenderingMethod method = selectedRendererUI.getImplementation();
if(setAsPreview) {
RenderingQueue queue = new RenderingQueue(method, new RenderingQueue.DefaultRepaintTask(method.getRenderedImage()), selectedRendererUI.getRepaintFrequency());
((IJResultsTable)table).setPreviewRenderer(queue);
((IJResultsTable)table).showPreview();
} else {
if(Recorder.record) {
Recorder.recordOption("imleft", Double.toString(left));
Recorder.recordOption("imtop", Double.toString(top));
Recorder.recordOption("imwidth", Double.toString(sizeX));
Recorder.recordOption("imheight", Double.toString(sizeY));
MacroParser.recordRendererUI(selectedRendererUI);
}
method.reset();
method.addToImage(xpos, ypos, z, dx, dz);
new RenderingQueue.DefaultRepaintTask(method.getRenderedImage()).run();
}
}
}
class RenderingDialog extends JDialog {
CardsPanel<IRendererUI> renderingMethods;
JButton sizeResultsButton;
JButton sizeAnalyzedImageButton;
JButton previewButton;
JButton okButton;
JButton cancelButton;
JButton defaultsButton;
DialogResult result = DialogResult.CANCELLED;
JTextField topTextField;
JTextField leftTextField;
JTextField sizeXTextField;
JTextField sizeYTextField;
double left, top, sizeX, sizeY;
boolean enablePreview;
int activeRendererIndex;
enum DialogResult {
CANCELLED, OK, PREVIEW;
}
public RenderingDialog(boolean preview, List<IRendererUI> knownRenderers, double left, double top, double sizeX, double sizeY) {
super(IJ.getInstance(), "Visualization", true);
this.activeRendererIndex = Integer.parseInt(Prefs.get("thunderstorm.rendering.index", "0"));
this.renderingMethods = new CardsPanel<IRendererUI>(knownRenderers, activeRendererIndex);
this.enablePreview = preview;
this.sizeX = Prefs.get("thunderstorm.rendering.imwidth", sizeX);
this.sizeY = Prefs.get("thunderstorm.rendering.imheight", sizeY);
this.left = Prefs.get("thunderstorm.rendering.imleft", left);
this.top = Prefs.get("thunderstorm.rendering.imtop", top);
layoutComponents();
}
@Override
public void setVisible(boolean b) {
super.setVisible(b);
}
public IRendererUI getSelectedRendererUI() {
return renderingMethods.getActiveComboBoxItem();
}
private void layoutComponents() {
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
getContentPane().setLayout(new GridBagLayout());
JPanel sizePanel = new JPanel(new GridBagLayout());
sizePanel.setBorder(new TitledBorder("ROI"));
JPanel sizeButtonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
sizeAnalyzedImageButton = new JButton("Auto size by analyzed image");
sizeAnalyzedImageButton.setEnabled(IJResultsTable.getResultsTable().getAnalyzedImage() != null);
sizeAnalyzedImageButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ImagePlus analyzedImage = IJResultsTable.getResultsTable().getAnalyzedImage();
left = top = 0;
sizeX = analyzedImage.getWidth();
sizeY = analyzedImage.getHeight();
leftTextField.setText(left + "");
topTextField.setText(top + "");
sizeXTextField.setText(sizeX + "");
sizeYTextField.setText(sizeY + "");
}
});
sizeResultsButton = new JButton("Auto size by results");
sizeResultsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
IJResultsTable rt = IJResultsTable.getResultsTable();
Rectangle rect = RenderingPlugIn.resizeByResults(rt.getColumnAsDoubles(LABEL_X, PIXEL),
rt.getColumnAsDoubles(LABEL_Y, PIXEL),
new EmptyRendererUI().magnification.getValue());
left = rect.getX();
top = rect.getY();
sizeX = rect.getWidth();
sizeY = rect.getHeight();
leftTextField.setText(left + "");
topTextField.setText(top + "");
sizeXTextField.setText(sizeX + "");
sizeYTextField.setText(sizeY + "");
if (rt.columnExists(LABEL_Z)) {
renderingMethods.getActiveComboBoxItem().set3D(true);
double[] zpos = rt.getColumnAsDoubles(LABEL_Z, NANOMETER);
renderingMethods.getActiveComboBoxItem().setZRange(VectorMath.min(zpos), VectorMath.max(zpos));
} else {
renderingMethods.getActiveComboBoxItem().set3D(false);
}
}
});
sizeButtonsPanel.add(sizeAnalyzedImageButton);
sizeButtonsPanel.add(sizeResultsButton);
sizePanel.add(sizeButtonsPanel, GridBagHelper.twoCols());
topTextField = new JTextField(Double.toString(top), 20);
leftTextField = new JTextField(Double.toString(left), 20);
sizeXTextField = new JTextField(Double.toString(sizeX), 20);
sizeYTextField = new JTextField(Double.toString(sizeY), 20);
sizePanel.add(new JLabel("Left top x [px]:"), GridBagHelper.leftCol());
sizePanel.add(leftTextField, GridBagHelper.rightCol());
sizePanel.add(new JLabel("Left top y [px]:"), GridBagHelper.leftCol());
sizePanel.add(topTextField, GridBagHelper.rightCol());
sizePanel.add(new JLabel("Width [px]:"), GridBagHelper.leftCol());
sizePanel.add(sizeXTextField, GridBagHelper.rightCol());
sizePanel.add(new JLabel("Height [px]:"), GridBagHelper.leftCol());
sizePanel.add(sizeYTextField, GridBagHelper.rightCol());
JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
okButton = new JButton("OK");
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
validateFields();
result = DialogResult.OK;
Prefs.set("thunderstorm.rendering.index", activeRendererIndex = renderingMethods.getActiveComboBoxItemIndex());
Prefs.set("thunderstorm.rendering.imwidth", sizeX);
Prefs.set("thunderstorm.rendering.imheight", sizeY);
Prefs.set("thunderstorm.rendering.imleft", left);
Prefs.set("thunderstorm.rendering.imtop", top);
dispose();
} catch(Exception ex) {
IJ.showMessage(ex.toString());
}
}
});
getRootPane().setDefaultButton(okButton);
cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
result = DialogResult.CANCELLED;
dispose();
}
});
defaultsButton = new JButton("Defaults");
defaultsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sizeXTextField.setText(sizeX + "");
sizeYTextField.setText(sizeY + "");
renderingMethods.setSelectedItemIndex(activeRendererIndex = 0);
AnalysisOptionsDialog.resetModuleUIs(renderingMethods.getItems());
}
});
buttonsPanel.add(defaultsButton);
if(enablePreview) {
previewButton = new JButton("Use for preview");
previewButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
validateFields();
result = DialogResult.PREVIEW;
Prefs.set("thunderstorm.rendering.index", activeRendererIndex = renderingMethods.getActiveComboBoxItemIndex());
Prefs.set("thunderstorm.rendering.imwidth", sizeX);
Prefs.set("thunderstorm.rendering.imheight", sizeY);
Prefs.set("thunderstorm.rendering.imleft", left);
Prefs.set("thunderstorm.rendering.imtop", top);
dispose();
} catch(Exception ex) {
IJ.showMessage(ex.toString());
}
}
});
buttonsPanel.add(previewButton);
}
buttonsPanel.add(okButton);
buttonsPanel.add(cancelButton);
add(sizePanel, GridBagHelper.leftCol());
JPanel cardsPanel = renderingMethods.getPanel("Method:");
cardsPanel.setBorder(new TitledBorder("Visualization options"));
add(cardsPanel, GridBagHelper.leftCol());
add(buttonsPanel, GridBagHelper.leftCol());
pack();
setLocationRelativeTo(null);
setResizable(false);
}
private void validateFields() {
left = Double.parseDouble(leftTextField.getText());
top = Double.parseDouble(topTextField.getText());
sizeX = Double.parseDouble(sizeXTextField.getText());
if(sizeX <= 0.0) {
throw new IllegalArgumentException("Image width must be positive.");
}
sizeY = Double.parseDouble(sizeYTextField.getText());
if(sizeY <= 0.0) {
throw new IllegalArgumentException("Image height must be positive.");
}
renderingMethods.getActiveComboBoxItem().readParameters();
}
}