package gdsc.utils; import java.awt.Checkbox; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Label; import java.awt.Point; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import gdsc.UsageTracker; import gdsc.core.ij.Utils; /*----------------------------------------------------------------------------- * GDSC Plugins for ImageJ * * Copyright (C) 2011 Alex Herbert * Genome Damage and Stability Centre * University of Sussex, UK * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *---------------------------------------------------------------------------*/ import ij.IJ; import ij.ImagePlus; import ij.Prefs; import ij.WindowManager; import ij.gui.GUI; import ij.gui.Line; import ij.gui.Overlay; import ij.gui.PointRoi; import ij.gui.Toolbar; import ij.measure.Calibration; import ij.plugin.MacroInstaller; import ij.plugin.frame.PlugInFrame; import ij.text.TextWindow; /** * Measures the distance between two consecutive points in XYZ. */ public class Measure3D extends PlugInFrame { private static final long serialVersionUID = 286478476052530844L; private static final String TITLE = "Measure 3D"; private static final String OPT_LOCATION = "Measure3D.location"; private static final String OPT_LOCATION_RESULTS = "Measure3D.location2"; private static Measure3D instance = null; private static TextWindow results = null; private int lastID = 0, lastX, lastY, lastZ, lastC, lastT; /** * Constructor */ public Measure3D() { super(TITLE); } /* * (non-Javadoc) * * @see ij.plugin.frame.PlugInFrame#run(java.lang.String) */ public void run(String arg) { UsageTracker.recordPlugin(this.getClass(), arg); if (WindowManager.getImageCount() == 0) { IJ.showMessage("No images opened."); return; } // Install the macro that is called when the image is clicked. // Do this each time since the toolbar could change. installTool(); if (instance != null) { if (!(instance.getTitle().equals(getTitle()))) { Measure3D oldInstance = (Measure3D) instance; Prefs.saveLocation(OPT_LOCATION, oldInstance.getLocation()); oldInstance.close(); } else { instance.toFront(); return; } } instance = this; IJ.register(Measure3D.class); WindowManager.addWindow(this); createFrame(); addKeyListener(IJ.getInstance()); pack(); Point loc = Prefs.getLocation(OPT_LOCATION); if (loc != null) setLocation(loc); else { GUI.center(this); } if (IJ.isMacOSX()) setResizable(false); setVisible(true); } private void installTool() { String name = "Measure 3D Tool"; if (Toolbar.getInstance().getToolId(name) == -1) { StringBuffer sb = new StringBuffer(); sb.append("macro 'Measure 3D Tool - L0ef7F0d22Fe722C00fT06103T5610D' {\n"); sb.append(" call('").append(this.getClass().getName()).append(".run');\n"); sb.append("};\n"); new MacroInstaller().install(sb.toString()); } Toolbar.getInstance().setTool(name); } public static void run() { if (instance != null) { instance.imageClicked(); } else { ImagePlus imp = getCurrentImage(); if (imp == null) return; // Create a new instance Measure3D p = new Measure3D(); p.run(""); } } private void imageClicked() { ImagePlus imp = getCurrentImage(); if (imp == null) return; Point p = imp.getCanvas().getCursorLoc(); if (p == null) return; int x = p.x; int y = p.y; int c = imp.getC(); int z = imp.getZ(); int t = imp.getT(); if (lastID == imp.getID() && lastC == c && lastT == t) { // This is the second point clicked on the same image and [C,T] // Reset the clicked point lastID = 0; for (int i = 1; i < labels.length; i++) labels[i].setText(""); // Compute the distance double dx = x - lastX; double dy = y - lastY; double dz = z - lastZ; double d = Math.sqrt(dx * dx + dy * dy + dz + dz); // Compute calibrated distance Calibration cal = imp.getCalibration(); double d2 = -1; if (cal != null) { // Assume X and Y units are the same. Check the z-unit. if (cal.getXUnit().equals(cal.getZUnit())) { dx *= cal.pixelWidth; dy *= cal.pixelHeight; dz *= cal.pixelDepth; d2 = Math.sqrt(dx * dx + dy * dy + dz + dz); } } // Record to a table record(imp, x, y, z, c, t, d, d2, cal.getXUnit()); // Overlay the line on the image if (overlayCheckbox.getState()) { Line roi = new Line(lastX, lastY, x, y); Overlay o = new Overlay(roi); imp.setOverlay(o); } } else { // This is the first point clicked on the same image and [C,T] // Display the clicked point labels[0].setText(imp.getTitle()); labels[1].setText(Integer.toString(x)); labels[2].setText(Integer.toString(y)); labels[3].setText(Integer.toString(z)); labels[4].setText(Integer.toString(c)); labels[5].setText(Integer.toString(t)); pack(); // Store the point that was clicked lastID = imp.getID(); lastX = x; lastY = y; lastZ = z; lastC = c; lastT = t; // Overlay the first point on the image if (overlayCheckbox.getState()) { PointRoi roi = new PointRoi(lastX, lastY); Overlay o = new Overlay(roi); imp.setOverlay(o); } } } private void createResultsTable() { if (results == null || !results.isVisible()) { results = new TextWindow(TITLE, "Image\tc\tt\tx1\ty1\tz1\tx2\ty2\tz2\tD (px)\tD\tUnits", "", 800, 400); Point loc = Prefs.getLocation(OPT_LOCATION_RESULTS); if (loc != null) results.setLocation(loc); results.addWindowListener(new WindowListener() { public void windowActivated(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowClosing(WindowEvent e) { Prefs.saveLocation(OPT_LOCATION_RESULTS, results.getLocation()); } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } }); } } private void record(ImagePlus imp, int x, int y, int z, int c2, int t, double d, double d2, String units) { createResultsTable(); StringBuilder sb = new StringBuilder(imp.getTitle()); sb.append('\t').append(lastC); sb.append('\t').append(lastT); sb.append('\t').append(lastX); sb.append('\t').append(lastY); sb.append('\t').append(lastZ); sb.append('\t').append(x); sb.append('\t').append(y); sb.append('\t').append(z); sb.append('\t').append(Utils.rounded(d)); if (d != -1) { sb.append('\t').append(Utils.rounded(d2)); sb.append('\t').append(units); } else { sb.append("\t\t"); } results.append(sb.toString()); } /** * @return The current image (must have an image canvas) */ private static ImagePlus getCurrentImage() { // NOTE: BUG // The ImageCanvas.mousePressed(MouseEvent e) eventually calls // Toolbar.getInstance().runMacroTool(toolID); // This runs the HSB_Picker if the tool is selected. // // This happens before WindowManager.setCurrentImage(...) is called // by the containing window that has been activated or brought to the front. // This means it is possible to click in a different image, raising the ImageCanvas event, // but have the HSL values sampled from the previous current image due to the use // of WindowManager.getCurrentImage(). // // This can be fixed by setting: // WindowManager.setCurrentWindow(win); // in the ImageCanvas.mousePressed(...) method. ImagePlus imp = WindowManager.getCurrentImage(); if (imp == null || imp.getCanvas() == null) return null; return imp; } /* * (non-Javadoc) * * @see ij.plugin.frame.PlugInFrame#close() */ public void close() { Prefs.saveLocation(OPT_LOCATION, getLocation()); instance = null; super.close(); } GridBagLayout mainGrid = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); int row = 0; Label[] labels; Checkbox overlayCheckbox; private void createFrame() { setLayout(mainGrid); // Build a grid that shows the last pressed position. labels = new Label[6]; ImagePlus imp = getCurrentImage(); String title = (imp == null) ? "" : imp.getTitle(); createLabelPanel(labels[0] = new Label(), "Image", title); createLabelPanel(labels[1] = new Label(), "X", ""); createLabelPanel(labels[2] = new Label(), "Y", ""); createLabelPanel(labels[3] = new Label(), "Z", ""); createLabelPanel(labels[4] = new Label(), "C", ""); createLabelPanel(labels[5] = new Label(), "T", ""); overlayCheckbox = new Checkbox("Overlay", true); add(overlayCheckbox, 0, 1); } private void createLabelPanel(Label labelField, String label, String value) { Label listLabel = new Label(label, 0); add(listLabel, 0, 1); if (labelField != null) { // labelField.setSize(fontWidth * 3, fontWidth); labelField.setText(value); add(labelField, 1, 1); } row++; } private void add(Component comp, int x, int width) { c.gridx = x; c.gridy = row; c.gridwidth = width; c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.WEST; mainGrid.setConstraints(comp, c); add(comp); } }