edu.uchc.octane.OctaneWindowControl.java Source code

Java tutorial

Introduction

Here is the source code for edu.uchc.octane.OctaneWindowControl.java

Source

//FILE:          OctaneWindowControl.java
//PROJECT:       Octane
//-----------------------------------------------------------------------------
//
// AUTHOR:       Ji Yu, jyu@uchc.edu, 1/16/11
//
// LICENSE:      This file is distributed under the BSD license.
//               License text is included with the source distribution.
//
//               This file is distributed in the hope that it will be useful,
//               but WITHOUT ANY WARRANTY; without even the implied warranty
//               of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//               IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//               CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//               INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES./**
//

package edu.uchc.octane;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.geom.GeneralPath;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.prefs.Preferences;

import javax.swing.JFileChooser;

import ij.IJ;
import ij.ImagePlus;
import ij.gui.HistogramWindow;
import ij.gui.ImageCanvas;
import ij.gui.Plot;
import ij.gui.PointRoi;
import ij.gui.Roi;
import ij.io.FileInfo;
import ij.process.FloatProcessor;
import ij.process.ShortProcessor;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

/**
 * Controller of the the octane window.
 */
public class OctaneWindowControl implements ClipboardOwner {

    final private static String SHOW_OVERLAY_KEY = "ShowOverlay";
    final private static String HISTOGRAM_BINS_KEY = "histogramBins";
    final private static String MSD_DELAY_KEY = "MsdDelay";
    final private static String NOTES_SCRIPT_KEY = "NotesScript";
    final private static String COMPENSATE_DRIFT_KEY = "compensateDrift";
    final private static String DEFAULT_EXPORT_PATH = "DefaultExportPath";

    public static Preferences prefs_ = GlobalPrefs.getRoot().node(OctaneWindowControl.class.getName());

    public static boolean compensateDrift_ = prefs_.getBoolean(COMPENSATE_DRIFT_KEY, false);
    public static boolean showOverlay_ = prefs_.getBoolean(SHOW_OVERLAY_KEY, false);
    public static int histogramBins_ = prefs_.getInt(HISTOGRAM_BINS_KEY, 20);
    public static int msdDelay_ = prefs_.getInt(MSD_DELAY_KEY, 4);
    public static String notesScript_ = prefs_.get(NOTES_SCRIPT_KEY, "");
    public static String lastSelectedFile_ = prefs_.get(DEFAULT_EXPORT_PATH, null);

    //static Color [] colorPalette_ = {Color.yellow, Color.green, Color.orange, Color.cyan, Color.magenta, Color.red};

    ImagePlus imp_ = null;
    TrajDataset dataset_ = null;
    protected String path_;
    protected Animator animator_ = null;
    OctaneWindow frame_ = null;

    /**
     * Constructor
     *
     * @param imp the image
     */
    public OctaneWindowControl(ImagePlus imp) {

        super();

        path_ = null;
        imp_ = imp;

        FileInfo fi = imp.getOriginalFileInfo();
        if (fi != null) {

            path_ = fi.directory;

        }

        imp.getCanvas().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {

                if (e.getClickCount() == 2) {

                    findMolecule();

                }
            }
        });

    }

    /**
     * Load dataset from default disk location
     *
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ClassNotFoundException the class not found exception
     */
    public void setup() throws IOException, ClassNotFoundException {

        loadDataset();

        createWindow();

    }

    /**
     * Setup window using provided dataset
     *
     * @param data the dataset
     */
    public void setup(TrajDataset data) {

        dataset_ = data;

        // saveDataset();

        createWindow();

    }

    /**
     * Setup window from positions of molecules.
     *
     * @param nodes positions of molecules
     */
    public void setup(SmNode[][] nodes) {

        dataset_ = TrajDataset.createDatasetFromNodes(nodes);

        // saveDataset();

        createWindow();

    }

    /**
     * Create and display a new Octane window
     */
    protected void createWindow() {
        frame_ = new OctaneWindow();

        if (imp_ != null) {
            frame_.setTitle("Octane - " + imp_.getTitle());
        }

        frame_.setController(this);

        frame_.setVisible(true);
    }

    /**
     * Returns the window object.
     *
     * @return the window
     */
    public OctaneWindow getWindow() {
        return frame_;
    }

    /**
     * Gets the dataset.
     *
     * @return the data
     */
    public TrajDataset getData() {

        return dataset_;

    }

    protected Rectangle getCurrentROI() {
        Rectangle rect;
        Roi roi = imp_.getRoi();
        if (roi != null && roi.isArea()) {
            rect = roi.getBounds();
        } else {
            imp_.killRoi();
            rect = imp_.getProcessor().getRoi();
            imp_.setRoi(roi);
        }
        return rect;
    }

    /**
     * Select trajectories that are located within the current ROI of the image window
     */
    protected void selectTrajectoriesWithinRoi() {
        Roi roi = imp_.getRoi();

        if (roi == null) {
            return;
        }

        if (roi instanceof PointRoi && ((PointRoi) roi).getNCoordinates() == 1) {

            int frame = imp_.getFrame();
            int x = ((PointRoi) roi).getXCoordinates()[0];
            int y = ((PointRoi) roi).getYCoordinates()[0];

            findMolecule(x, y, frame);

        } else {

            boolean firstSel = true;

            for (int i = 0; i < dataset_.getSize(); i++) {

                Trajectory t = dataset_.getTrajectoryByIndex(i);

                if (!t.deleted) {

                    for (int j = 0; j < t.size(); j++) {

                        if (roi.contains((int) t.get(j).x, (int) t.get(j).y)) {

                            if (firstSel) {

                                frame_.selectTrajectoryByIndex(i);
                                firstSel = false;

                            } else {

                                frame_.addTrajectoriesToSelection(i);

                            }

                            break;
                        }
                    }
                }
            }
        }
    }

    /**
     * Copy selected trajectories to system clipboard
     */
    protected void copySelectedTrajectories() {

        StringBuilder buf = new StringBuilder();
        int[] selected = frame_.getTrajsTable().getSelectedTrajectories();
        Trajectory traj;

        for (int i = 0; i < selected.length; i++) {

            traj = dataset_.getTrajectoryByIndex(selected[i]);

            for (int j = 0; j < traj.size(); j++) {

                buf.append(String.format("%10.4f, %10.4f, %10d, %5d%n", traj.get(j).x, traj.get(j).y,
                        traj.get(j).frame, i));

            }
        }

        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection contents = new StringSelection(buf.toString());
        clipboard.setContents(contents, this);
    }

    /**
     * Find the trajectory that contains the specified particle  
     * @param x The x coordinate of the particle
     * @param y The y coordinate of the particle
     * @param f The frame number of the particle
     */
    protected void findMolecule(int x, int y, int f) {
        int index = 0;
        int fi = 0;
        boolean found = false;
        int lastIndex = dataset_.getSize();
        while (!found && index < lastIndex) {
            Trajectory t = dataset_.getTrajectoryByIndex(index);
            if (t != null && t.size() > 0 && t.get(0).frame <= f && t.get(t.size() - 1).frame >= f) {
                fi = f - t.get(0).frame;
                if (fi >= t.size()) {
                    fi = t.size() - 1;
                }
                while (t.get(fi).frame > f) {
                    fi--;
                }
                if (t.get(fi).frame == f) {
                    if (Math.abs(t.get(fi).x - x) < 2.5 && Math.abs(t.get(fi).y - y) < 2.5) {
                        found = true;
                    }
                }
            }
            index++;
        }
        if (found) {
            frame_.selectTrajectoryAndNodeByIndex(index - 1, fi);
        }
    }

    /**
     * Find the trajectory that contains the particle specified by the cursor  
     */
    protected void findMolecule() {
        ImageCanvas canvas = imp_.getCanvas();
        Point p = canvas.getCursorLoc();
        int frame = imp_.getCurrentSlice();

        findMolecule(p.x, p.y, frame);
    }

    /**
     * Draw trajectory overlay on images 
     */
    protected void drawOverlay() {
        if (!showOverlay_) {
            imp_.setOverlay(null);
            return;
        }
        GeneralPath path = new GeneralPath();
        for (int i = 0; i < dataset_.getSize(); i++) {
            Trajectory v = dataset_.getTrajectoryByIndex(i);
            if (v.marked) {
                // path.append(new Arc2D.Double(v.get(0).x-0.15,v.get(0).y,0.3,0.3,0,360,Arc2D.OPEN), false);
                path.moveTo(v.get(0).x, v.get(0).y);
                for (int j = 1; j < v.size(); j++) {
                    path.lineTo(v.get(j).x, v.get(j).y);
                }
                // path.append(new Rectangle2D.Double(v.get(v.size()-1).x-0.15,v.get(v.size()-1).y-0.15,0.3,0.3), false);
            }
        }
        imp_.setOverlay(path, Color.yellow, new BasicStroke(0.1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    }

    /**
     * Draw a small box around the current particle 
     */
    protected void drawBox() {
        SmNode node = frame_.getNodesTable().getCurrentNode();
        int x, y;
        if (node != null && imp_ != null) {
            imp_.setSlice(node.frame);
            x = (int) Math.round(node.x);
            y = (int) Math.round(node.y);
            imp_.setRoi(x - 5, y - 5, 11, 11);
            ImageCanvas canvas = imp_.getCanvas();
            Rectangle r = canvas.getSrcRect();
            int sx = canvas.screenX(x);
            int sy = canvas.screenY(y);
            if (sx < 4 || sx > r.width - 5 || sy < 4 || sy > r.height - 5) {
                int nx = Math.max(x - r.width / 2, 0);
                int ny = Math.max(y - r.height / 2, 0);
                if (nx + r.width > imp_.getWidth()) {
                    nx = imp_.getWidth() - r.width;
                }
                if (ny + r.height > imp_.getHeight()) {
                    ny = imp_.getHeight() - r.height;
                }
                canvas.setSourceRect(new Rectangle(nx, ny, r.width, r.height));
                imp_.updateAndDraw();
            }
        }
    }

    /**
     * Plot selected trajectories in a SVG file
     *  
     *  @param file The SVG file.
     */
    protected void plotTrajectoryToSVG(File file) {

        TrajectoryPlot tp = new TrajectoryPlot(dataset_);
        tp.generateSVG(getCurrentROI(), file);

    }

    /**
     * Construct PALM image.
     * 
     * @param useStack whether to render a 3D PALM using image stack
     */
    protected void constructPalm(boolean useStack) {

        if (PalmParameters.openDialog(dataset_, useStack)) {

            Palm palm = new Palm(dataset_);
            int[] selected = frame_.getTrajsTable().getSelectedTrajectoriesOrAll();

            // palm.setCorrectDrift(dlg.getNextBoolean());
            palm.constructPalm(imp_, selected);
        }
    }

    /**
     * Construct mobility map.
     */
    public void constructMobilityMap() {
        FlowAnalysis fa = new FlowAnalysis(dataset_);
        fa.constructMobilityMap(imp_, frame_.getTrajsTable().getSelectedTrajectoriesOrAll());
    }

    /**
     * Gets the Imagej image.
     *
     * @return the imageplus
     */
    public ImagePlus getImp() {
        return imp_;
    }

    /**
     * Select all (un)marked trajectory in the trajectory table.
     *
     * @param b select marked if true, unmarked if false
     */
    public void selectMarked(boolean b) {
        frame_.selectTrajectoryByIndex(-1); //clear selection
        for (int i = 0; i < dataset_.getSize(); i++) {
            if (dataset_.getTrajectoryByIndex(i).marked == b) {
                frame_.addTrajectoriesToSelection(i);
            }
        }
    }

    /**
     * Rebuild trajectories.
     */
    public void rebuildTrajectories() {
        if (TrackingParameters.openDialog(dataset_.pixelSize_) == true) {
            dataset_.reTrack();
            frame_.getTrajsTable().setData(dataset_);
        }
    }

    /**
     * Show trajectory length histogram.
     */
    public void showLengthHistogram() {
        int[] selected = frame_.getTrajsTable().getSelectedTrajectoriesOrAll();
        short[] d = new short[selected.length];

        int min = 10000;
        int max = -1;
        for (int i = 0; i < selected.length; i++) {
            Trajectory t = dataset_.getTrajectoryByIndex(selected[i]);
            d[i] = (short) (t.getLength());
            if (d[i] > max) {
                max = d[i];
            }
            if (d[i] < min) {
                min = d[i];
            }
        }
        ShortProcessor ip = new ShortProcessor(1, d.length, d, null);
        ImagePlus imp = new ImagePlus("", ip);
        HistogramWindow hw = new HistogramWindow("Trajectory Length Histogram", imp, max - min);
        hw.setVisible(true);
        imp.close();
    }

    /**
     * Show fitting residue histogram.
     */
    public void showResidueHistogram() {
        int[] selected = frame_.getTrajsTable().getSelectedTrajectoriesOrAll();
        //ArrayList<Double> d = new ArrayList<Double>();
        int numOfNodes = 0;
        for (int i = 0; i < selected.length; i++) {
            numOfNodes += dataset_.getTrajectoryByIndex(selected[i]).size();
        }
        double[] d = new double[numOfNodes];
        int cnt = 0;
        for (int i = 0; i < selected.length; i++) {
            Iterator<SmNode> itr = dataset_.getTrajectoryByIndex(selected[i]).iterator();
            while (itr.hasNext()) {
                d[cnt++] = itr.next().residue;
            }
        }
        FloatProcessor ip = new FloatProcessor(1, d.length, d);
        ImagePlus imp = new ImagePlus("", ip);
        HistogramWindow hw = new HistogramWindow("Residue Histogram", imp, histogramBins_);
        hw.setVisible(true);
        imp.close();
    }

    /**
     * Show displacement histogram.
     * 
     * @param stepSize the stepsize for calculating the displacement
     */
    public void showDisplacementHistogram(int stepSize) {
        int[] selected = frame_.getTrajsTable().getSelectedTrajectoriesOrAll();
        ArrayList<Double> dl = new ArrayList<Double>();
        for (int i = 0; i < selected.length; i++) {
            Trajectory t = dataset_.getTrajectoryByIndex(selected[i]);
            for (int j = 0; j < t.size() - stepSize; j++) {
                int k = j + 1;
                int frame = t.get(j).frame;
                while (k < t.size()) {
                    if (t.get(k).frame - frame < stepSize) {
                        k++;
                    } else if (t.get(k).frame - frame == stepSize) {
                        dl.add(t.get(j).distance(t.get(k)));
                        break;
                    } else {
                        break;
                    }
                }
            }
            //IJ.showProgress(i, selected.length);
        }
        double[] d = new double[dl.size()];
        for (int i = 0; i < dl.size(); i++) {
            d[i] = dl.get(i).doubleValue() * dataset_.getPixelSize();
        }
        if (d.length <= 1) {
            IJ.error(GlobalPrefs.PACKAGE_NAME, "Not enough data point. Stepsize too large?");
            return;
        }
        FloatProcessor ip = new FloatProcessor(1, d.length, d);
        ImagePlus imp = new ImagePlus("", ip);
        HistogramWindow hw = new HistogramWindow("Displacement Histogram", imp,
                DspHistogramParameters.histogramBins_, DspHistogramParameters.dspHistogramMin_,
                DspHistogramParameters.dspHistogramMax_);
        hw.setVisible(true);
        imp.close();
    }

    /**
     * Show directional displacement histogram.
     * 
     * @param stepSize the stepsize for calculating the displacement
     * @param dir direction of the displacement in degrees. 0 is up
     */
    public void showDirectionalDisplacementHistogram(int stepSize, double dir) {
        double[] v = new double[2];
        v[0] = Math.sin(dir * Math.PI / 180);
        v[1] = -Math.cos(dir * Math.PI / 180);
        int[] selected = frame_.getTrajsTable().getSelectedTrajectoriesOrAll();
        ArrayList<Double> dl = new ArrayList<Double>();
        for (int i = 0; i < selected.length; i++) {
            Trajectory t = dataset_.getTrajectoryByIndex(selected[i]);
            for (int j = 0; j < t.size() - stepSize; j++) {
                int k = j + 1;
                int frame = t.get(j).frame;
                while (k < t.size()) {
                    if (t.get(k).frame - frame < stepSize) {
                        k++;
                    } else if (t.get(k).frame - frame == stepSize) {
                        dl.add((t.get(j).x - t.get(k).x) * v[0] + (t.get(j).y - t.get(k).y) * v[1]);
                        break;
                    } else {
                        break;
                    }
                }
            }
            IJ.showProgress(i, selected.length);
        }
        double[] d = new double[dl.size()];
        for (int i = 0; i < dl.size(); i++) {
            d[i] = dl.get(i).doubleValue();
        }
        if (d.length <= 1) {
            IJ.error(GlobalPrefs.PACKAGE_NAME, "Not enough data point. Stepsize too large?");
            return;
        }
        FloatProcessor ip = new FloatProcessor(1, d.length, d);
        ImagePlus imp = new ImagePlus("", ip);
        HistogramWindow hw = new HistogramWindow("Directional Displacement Histogram", imp, histogramBins_);
        hw.setVisible(true);
        imp.close();
    }

    /**
     * Show mean square displacement.
     */
    public void showMSD(int maxSteps) {
        int[] selected = frame_.getTrajsTable().getSelectedTrajectoriesOrAll();
        ArrayList<SummaryStatistics> stat = new ArrayList<SummaryStatistics>();

        for (int i = 0; i < selected.length; i++) {

            Trajectory t = dataset_.getTrajectoryByIndex(selected[i]);

            for (int j = 0; j < t.size() - 1; j++) {

                int frame = t.get(j).frame;
                for (int k = j + 1; k < t.size(); k++) {

                    int deltaframe = t.get(k).frame - frame;
                    if (deltaframe <= maxSteps) {

                        while (deltaframe > stat.size()) {

                            stat.add(new SummaryStatistics());
                        }

                        stat.get(deltaframe - 1).addValue(t.get(j).distance2(t.get(k)));

                    }
                }
            }

            IJ.showProgress(i, selected.length);
        }

        double[] x = new double[stat.size()];
        double[] y = new double[stat.size()];
        double[] e = new double[stat.size()];

        if (stat.size() > 0) {

            double ps = dataset_.getPixelSize() * dataset_.getPixelSize();

            for (int i = 0; i < stat.size(); i++) {

                x[i] = 1.0 + i;
                if (stat.get(i).getN() > 1) {

                    y[i] = stat.get(i).getMean() * ps;
                    e[i] = stat.get(i).getStandardDeviation() / Math.sqrt(stat.get(i).getN()) * ps;

                }
            }

            Plot plotWin = new Plot("MSD Plot", "T/T-frame", "MSD (nm^2)", x, y);
            plotWin.addPoints(x, y, Plot.BOX);
            plotWin.addErrorBars(e);
            plotWin.show();

        }
    }

    /**
     * Test Ergodicity 
     */
    public void showErgodicityTest(int maxSteps) {
        int[] selected = frame_.getTrajsTable().getSelectedTrajectoriesOrAll();
        ArrayList<SummaryStatistics> stat = new ArrayList<SummaryStatistics>();

        for (int i = 0; i < maxSteps; i++) {
            stat.add(new SummaryStatistics());
        }

        for (int i = 0; i < selected.length; i++) {

            Trajectory t = dataset_.getTrajectoryByIndex(selected[i]);
            int firstFrame = t.get(0).frame;

            for (int j = 0; j < t.size() - 1; j++) {

                int frame = t.get(j).frame;
                if (frame - firstFrame < maxSteps && t.get(j + 1).frame - frame == 1) {
                    stat.get(frame - firstFrame).addValue(t.get(j + 1).distance2(t.get(j)));
                }
            }
            IJ.showProgress(i, selected.length);
        }

        double[] x = new double[stat.size()];
        double[] y = new double[stat.size()];
        double[] e = new double[stat.size()];

        if (stat.size() > 0) {
            double ps = dataset_.getPixelSize() * dataset_.getPixelSize();

            for (int i = 0; i < stat.size(); i++) {
                x[i] = 1.0 + i;
                if (stat.get(i).getN() > 1) {
                    y[i] = stat.get(i).getMean() * ps;
                    e[i] = stat.get(i).getStandardDeviation() / Math.sqrt(stat.get(i).getN() - 1) * ps;
                }
            }

            Plot plotWin = new Plot("Ergodicity Test", "dt (frame)", "D^2 (nm^2)", x, y);
            plotWin.addPoints(x, y, Plot.BOX);
            plotWin.addErrorBars(e);
            plotWin.show();
        }
    }

    /**
     * Animate the current trajectory.
     */
    public void animate() {

        if (animator_ == null) {

            animator_ = new Animator(imp_);
            animator_.setLoop(true);

        }

        int index = frame_.getTrajsTable().getSelectedTrajectoryIndex();
        if (index >= 0) {

            animator_.animate(dataset_.getTrajectoryByIndex(index));

        }

    }

    /**
     * Stop the animation
     */
    public void stopAnimation() {
        if (animator_ == null)
            return;

        animator_.stopAnimation();
    }

    /**
     * Delete all selected trajectories from trajectory table.
     */
    public void deleteSelectedTrajectories() {
        int[] selected = frame_.getTrajsTable().getSelectedTrajectories();
        for (int i = 0; i < selected.length; i++) {
            dataset_.getTrajectoryByIndex(selected[i]).deleted = true;
        }
    }

    /**
     * Returns the default pathname for saving the dataset.
     *
     * @return the pathname
     */
    String defaultSaveFilename() {

        return path_ + File.separator + imp_.getTitle() + ".dataset";

    }

    /**
     * Save the dataset to disk.
     *
     * @param pathname the pathname
     */
    public void saveDataset(String pathname) {
        try {

            File file = new File(pathname);

            dataset_.saveDataset(file);

        } catch (IOException e) {

            IJ.error(GlobalPrefs.PACKAGE_NAME, "IOError: Failed to save file.");
        }

    }

    /**
     * Save the dataset using the default pathname (imagetitle + .dataset).
     */
    public void saveDataset() {

        saveDataset(defaultSaveFilename());

    }

    /**
     * Load dataset from disk.
     *
     * @param pathname the pathname
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ClassNotFoundException the class not found exception
     */
    public void loadDataset(String pathname) throws IOException, ClassNotFoundException {

        File file = new File(pathname);
        dataset_ = TrajDataset.loadDataset(file);

    }

    /**
     * Load dataset from disk at default save location.
     *
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ClassNotFoundException the class not found exception
     */
    public void loadDataset() throws IOException, ClassNotFoundException {

        loadDataset(defaultSaveFilename());

    }

    /* (non-Javadoc)
     * @see java.awt.datatransfer.ClipboardOwner#lostOwnership(java.awt.datatransfer.Clipboard, java.awt.datatransfer.Transferable)
     */
    @Override
    public void lostOwnership(Clipboard arg0, Transferable arg1) {
        // 
    }

    /**
     * Export dataset to text.
     * Each molecule occupy one line: x, y, frame, residue
     * 
     * @param file the file
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void exportNodes(File file) throws IOException {

        dataset_.writePositionsToText(file);

    }

    /**
     * Export selected trajectories to text.
     * Each molecule occupy one line: frame, x, y, z, height, trackIdx
     * 
     * @param file the file
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void exportTrajectories(File file) throws IOException {

        BufferedWriter bw = new BufferedWriter(new FileWriter(file));
        int[] selected = frame_.getTrajsTable().getSelectedTrajectories();
        Trajectory traj;
        bw.append("# Frame, X, Y, Z, Intensity, TrackIDX\n");

        for (int i = 0; i < selected.length; i++) {

            traj = dataset_.getTrajectoryByIndex(selected[i]);

            for (int j = 0; j < traj.size(); j++) {

                SmNode n = traj.get(j);
                bw.append(n.toString());
                bw.append(", " + i + "\n");

            }
        }

        bw.close();
    }

    /**
     * Plot intensity transients of selected trajectories
     * 
     */
    public void plotTransients() {

    }

    /**
     * Plot the drift calibration  
     */
    public void displayDrift() {
        int[] selected = frame_.getTrajsTable().getSelectedTrajectories();

        dataset_.estimateDrift(selected);

        double[] dx = dataset_.getDriftX();
        double[] dy = dataset_.getDriftY();
        double[] dz = dataset_.getDriftZ();
        double[] f = new double[dx.length];

        for (int i = 0; i < f.length; i++) {
            f[i] = i;
        }

        Plot plotWinX = new Plot("X-Drift", "T/T-frame", "Drift (pixel)", f, dx);
        //plotWin.addPoints(x, y, Plot.BOX);
        //plotWin.addErrorBars(e);
        plotWinX.show();

        Plot plotWinY = new Plot("Y-Drift", "T/T-frame", "Drift (pixel)", f, dy);
        plotWinY.show();

        Plot plotWinZ = new Plot("Z-Drift", "T/T-frame", "Drift (pixel)", f, dz);
        plotWinZ.show();
    }

    /**
     * Ask user to choose a file and import drift calibration data from the file 
     */
    public void importDriftData() {

        JFileChooser fc = new JFileChooser();
        if (fc.showOpenDialog(IJ.getApplet()) == JFileChooser.APPROVE_OPTION) {

            try {

                dataset_.importDriftData(fc.getSelectedFile());

            } catch (IOException e) {

                IJ.error("An IO error occured reading file: " + fc.getSelectedFile().getName());
            }
        }
    }

    public static void savePrefs() {

        prefs_.putBoolean(COMPENSATE_DRIFT_KEY, compensateDrift_);
        prefs_.putBoolean(SHOW_OVERLAY_KEY, showOverlay_);
        prefs_.putInt(HISTOGRAM_BINS_KEY, histogramBins_);
        prefs_.putInt(MSD_DELAY_KEY, msdDelay_);
        prefs_.put(NOTES_SCRIPT_KEY, notesScript_);
        prefs_.put(DEFAULT_EXPORT_PATH, lastSelectedFile_);
    }
}