/*
* Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fhcrc.cpl.viewer.gui;
import org.apache.log4j.Logger;
import org.fhcrc.cpl.toolbox.proteomics.gui.IntensityPlot;
import org.fhcrc.cpl.viewer.util.SharedProperties;
import org.fhcrc.cpl.viewer.feature.*;
import org.fhcrc.cpl.viewer.feature.extraction.FeatureFinder;
import org.fhcrc.cpl.viewer.feature.extraction.PeakCombiner;
import org.fhcrc.cpl.viewer.feature.extraction.strategy.BaseFeatureStrategy;
import org.fhcrc.cpl.toolbox.proteomics.feature.extraInfo.IsotopicLabelExtraInfoDef;
import org.fhcrc.cpl.toolbox.proteomics.MSRun;
import org.fhcrc.cpl.viewer.Application;
import org.fhcrc.cpl.toolbox.gui.ScrollableImage;
import org.fhcrc.cpl.toolbox.gui.ListenerHelper;
import org.fhcrc.cpl.viewer.gui.SurfaceFrame;
import org.fhcrc.cpl.toolbox.*;
import org.fhcrc.cpl.toolbox.datastructure.Pair;
import org.fhcrc.cpl.toolbox.datastructure.*;
import org.fhcrc.cpl.toolbox.proteomics.Scan;
import org.fhcrc.cpl.toolbox.proteomics.feature.Spectrum;
import org.fhcrc.cpl.toolbox.proteomics.feature.FeatureSet;
import org.fhcrc.cpl.toolbox.proteomics.feature.Feature;
import org.swixml.SwingEngine;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
/**
* User: mbellew
* Date: May 24, 2004
* Time: 4:30:30 PM
*/
public class MSDetailPanel extends ScrollableImage
{
Logger _log = Logger.getLogger(MSDetailPanel.class);
static final String LOW_THRESHOLD = "MSDetailPanel.lowThreshold";
static final String SCALE = "MSDetailPanel.scale";
static final String COLORSCHEME = "MSDetailPanel.colorscheme";
static final String PREPROCESS = "MSDetailPanel.preprocess";
static final String FINDFEATURES = "MSDetailPanel.findfeatures";
static final String SHOWCHARTLINE = "MSDetailPanel.showChartLine";
static Font _smallFont = Font.decode("Verdana PLAIN 9");
//hardcoded initial dimensions
protected Dimension mPanelDimension = new Dimension(43,2048);
Tree2D _featuresVisible = null;
java.util.List _extractedFeatures = null;
class ImageParameters implements Cloneable
{
FloatRange _mzRange = new FloatRange();
// float _mzMin;
int _yScale;
int _scanMin;
int _scanCount;
Rectangle _rectZoom = new Rectangle();
protected Object clone()
{
try
{
ImageParameters c = (ImageParameters)super.clone();
c._rectZoom = (Rectangle)_rectZoom.clone();
return c;
}
catch (CloneNotSupportedException x)
{
return null;
}
}
};
ImageParameters _curr = new ImageParameters();
Thread _renderThread = null;
ListenerHelper helper = new ListenerHelper(this);
public MSDetailPanel()
{
super("MSDetailPanel");
setToolTipText("I'm a tool tip");
Application app = Application.getInstance();
helper.addListener(app, "repaint_propertyChange", MSDetailPanel.SHOWCHARTLINE);
helper.addListener(app, "repaint_propertyChange", SharedProperties.CHART_RANGE);
helper.addListener(app, "repaint_propertyChange", SharedProperties.FEATURE_RANGES);
helper.addListener(app, "repaint_propertyChange", "featureSelector");
helper.addListener(app, "update_propertyChange", MSDetailPanel.FINDFEATURES);
helper.addListener(app, "update_propertyChange", SharedProperties.MS_RUN);
helper.addListener(app, "update_propertyChange", SharedProperties.SELECTED_POINT);
helper.addListener(app, "update_propertyChange", FeatureExtractor.DEFAULT_EXTRACTOR_PROPERTYNAME);
helper.addListener(app, "update_propertyChange", LOW_THRESHOLD);
helper.addListener(app, "update_propertyChange", SCALE);
helper.addListener(app, "update_propertyChange", COLORSCHEME);
helper.addListener(app, "update_propertyChange", PREPROCESS);
helper.addListener(this, "_componentResized");
helper.addListener(this, "_mouseClicked");
}
static boolean scheduledUpdate = false;
public void update_propertyChange(PropertyChangeEvent event)
{
if (scheduledUpdate)
return;
scheduledUpdate = true;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
scheduledUpdate = false;
updateImage();
}
});
}
public void repaint_propertyChange(PropertyChangeEvent event)
{
if (getFindFeatures() && SharedProperties.CHART_RANGE.equals(event.getPropertyName()))
{
Spectrum.Peak p = (Spectrum.Peak)ApplicationContext.getProperty(SharedProperties.SELECTED_POINT);
if (null != getImage() && null == _renderThread && null != p)
{
// Is it scan at a time??, then update features
// this is hacky
Class c = (Class) ApplicationContext.getProperty(FeatureExtractor.DEFAULT_EXTRACTOR_PROPERTYNAME);
if (c == FeatureStrategyWavelet.class || c == FeatureStrategyCentroided.class)
{
MSRun run = (MSRun)ApplicationContext.getProperty(SharedProperties.MS_RUN);
Pair chartRange = (Pair)event.getNewValue();
if (null != chartRange && ((Spectrum.Peak)chartRange.first).scan == ((Spectrum.Peak)chartRange.second).scan)
{
int scan = ((Spectrum.Peak)chartRange.first).scan;
int scanIndex = run.getIndexForScanNum(scan);
if (scanIndex < 0) scanIndex = -(scanIndex+1);
ImageGenerator gen = new ImageGenerator(run, scanIndex, p.mz);
gen._findDetailFeatures(_curr);
}
}
}
}
repaint();
}
public void _componentResized(ComponentEvent event)
{
updateImage();
}
protected MSRun getRun()
{
return (MSRun)ApplicationContext.getProperty(SharedProperties.MS_RUN);
}
public void _mouseClicked(MouseEvent event)
{
if (null == _curr || 0 == _curr._yScale)
return;
MSRun run = getRun();
Feature fBest = hitTest(event);
if (fBest != null)
{
ApplicationContext.setProperty(SharedProperties.SELECTED_POINT, fBest);
ApplicationContext.setProperty(SharedProperties.SELECTED, fBest);
if (null != run)
{
int n = run.getIndexForScanNum(fBest.scan, true);
ApplicationContext.setProperty(SharedProperties.MS_SCAN, run.getScan(n));
}
}
else
{
if (null == run)
return;
int x = event.getX();
int y = getHeight() - event.getY() - 1;
if (x + _curr._scanMin >= 1 && x + _curr._scanMin <= run.getScanCount())
{
MSRun.MSScan scan = run.getScan(x + _curr._scanMin);
ApplicationContext.setProperty(SharedProperties.MS_SCAN, scan);
float mz = y / (float)_curr._yScale + _curr._mzRange.min;
// Spectrum.Peak selectedPoint = (Spectrum.Peak)app.getProperty(SharedProperties.SELECTED_POINT);
// if (null != selectedPoint)
// app.setProperty(SharedProperties.SELECTED_POINT, new Spectrum.Peak(x + _scanMin, selectedPoint.mz, 0F));
// else
ApplicationContext.setProperty(SharedProperties.SELECTED_POINT, new Spectrum.Peak(scan.getNum(), mz, 0F));
}
}
}
public String getToolTipText(MouseEvent event)
{
if (null == _curr || 0 == _curr._yScale)
return "";
int height = getHeight();
int y = height - event.getY() - 1;
int x = event.getX();
MSRun run = getRun();
if (null == run)
return "";
//check the scan under the mouse event to make sure it's within range for the run
int scanIndex = x + _curr._scanMin;
if (scanIndex >= run.getScanCount())
return "";
MSRun.MSScan scan = run.getScan(scanIndex);
float mz = (float)y / _curr._yScale + _curr._mzRange.min;
String text = "" + scan.getNum() + ", " + mz;
Feature fBest = hitTest(event);
if (null != fBest)
{
StringBuffer sb = new StringBuffer("<html>" +
fBest.scan + "<br>" + fBest.mz + "mz (" + fBest.getMass() + ")<br>" + fBest.charge + "+<br>" +
((int)fBest.intensity) + "i" +
(0 == fBest.background ? "" : " / " + Math.round(fBest.background) + "bg") +
(0 == fBest.getMedian() ? "" : " / " + Math.round(fBest.getMedian()) + "md"));
if (fBest instanceof Feature)
{
Feature fr = (Feature)fBest;
if (fr.getScanFirst() != fr.getScanLast())
sb.append("<br>" + fr.getScanFirst() + "-" + fr.getScanLast());
}
if (null != fBest.getDescription())
sb.append("<br>" + fBest.getDescription());
sb.append("</html>");
text = sb.toString();
}
return text;
}
private Feature hitTest(MouseEvent event)
{
if (null == _featuresVisible)
return null;
int height = getHeight();
int y = height - event.getY() - 1;
int x = event.getX();
int scanClicked = _curr._scanMin + x;
MSRun run = (MSRun)ApplicationContext.getProperty(SharedProperties.MS_RUN);
float mz = (float)y / _curr._yScale + _curr._mzRange.min;
Feature fBest = null;
double dist = 0.2;
ArrayList near = _featuresVisible.getPoints(scanClicked - 3, mz - 1, scanClicked + 3, mz + 1);
for (Iterator it = near.iterator(); it.hasNext();)
{
Feature f = (Feature)it.next();
double d = Math.abs(mz - f.mz);
if (d < dist)
{
int scanIndex = run.getIndexForScanNum(f.scan, true);
if (Math.abs(scanIndex - scanClicked) <= 3)
{
dist = d;
fBest = f;
}
}
}
return fBest;
}
public Dimension getPreferredSize()
{
return super.getPreferredSize();
}
protected synchronized void updateImage()
{
this._featuresVisible = null;
this.setImage(null, null);
Spectrum.Peak p = (Spectrum.Peak)ApplicationContext.getProperty(SharedProperties.SELECTED_POINT);
if (null == p)
return;
MSRun run = (MSRun) ApplicationContext.getProperty(SharedProperties.MS_RUN);
if (null == run)
return;
ApplicationContext.setProperty(SharedProperties.ZOOM_REGION, null);
if (null != _renderThread && _renderThread.isAlive())
{
_renderThread.interrupt();
_renderThread = null;
}
int index = run.getIndexForScanNum(p.scan, true);
_renderThread = new Thread(new ImageGenerator(run, index, (double)p.mz));
_renderThread.setPriority(3);
_renderThread.start();
}
public void setImage(Image image, ImageParameters p)
{
_curr = p;
super.setImage(image);
}
static Object _imageGeneratorRunLock = new Object();
private class ImageGenerator implements Runnable
{
MSRun _run;
int _scanIndex;
double _mz;
int _lowThreshold;
boolean _process = false;
boolean _findFeatures = false;
String _scale = "log";
public ImageGenerator(MSRun run, int scanIndex, double mz)
{
_run = run;
_scanIndex = scanIndex;
_mz = mz;
_lowThreshold = getLowThreshold();
_scale = getScale();
_process = getPreprocess();
_findFeatures = getFindFeatures();
}
public void run()
{
try
{
final ImageParameters newImage = new ImageParameters();
// need to synchronize this or we will trample member variables
synchronized (_imageGeneratorRunLock)
{
_generateDetailImage(newImage);
if (_findFeatures)
_findDetailFeatures(newImage);
}
}
finally
{
synchronized(MSDetailPanel.this)
{
if (_renderThread == Thread.currentThread())
_renderThread = null;
}
}
}
private void _generateDetailImage(ImageParameters newImage) // Rectangle rectZoom /* OUT */)
{
_log.debug("_generateDetailImage() thread " + Thread.currentThread());
Thread me = Thread.currentThread();
boolean centroided = _run.getHeaderInfo().getDataProcessing().getCentroided() == 1;
int width = getWidth();
newImage._scanCount = width - 3;
int height = getHeight();
// want range of about +/-10 but keep yscale < 30
float mzRange = 20;
newImage._yScale = (int)Math.min(height / mzRange, 36);
mzRange = height / newImage._yScale;
newImage._scanMin = Math.max(0, _scanIndex - newImage._scanCount / 2);
int scanMax = Math.min(newImage._scanMin + newImage._scanCount, _run.getScanCount());
newImage._scanCount = scanMax - newImage._scanMin;
newImage._mzRange.min = (int)Math.floor(_mz - (mzRange / 2));
newImage._mzRange.max = newImage._mzRange.min + mzRange + 1;
Rectangle rect = new Rectangle(Math.max(10,width), Math.max(10,height));
BufferedImage img = new BufferedImage((int)rect.getWidth(), (int)rect.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g = (Graphics2D)img.getGraphics();
g.setFont(_smallFont);
g.setBackground(Color.WHITE);
g.clearRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
// collect all the points in +/- 10 scans and within +/- 10D
FloatArray x = new FloatArray();
FloatArray y = new FloatArray();
FloatArray z = new FloatArray();
newImage._rectZoom.x = newImage._scanMin;
newImage._rectZoom.y = (int)Math.ceil(newImage._mzRange.min);
newImage._rectZoom.width = newImage._scanCount;
newImage._rectZoom.height = (int)Math.round(newImage._mzRange.max - newImage._mzRange.min);
FloatRange mzWindow = new FloatRange(newImage._mzRange.min - 2, newImage._mzRange.max + 2);
MSRun.MSScan[] scans = new MSRun.MSScan[scanMax - newImage._scanMin];
for (int s = 0; s < scanMax - newImage._scanMin; s++)
scans[s] = _run.getScan(newImage._scanMin + s);
float[][] spectra = new float[scans.length][];
for (int s = 0; s < scans.length; s++)
{
if (me.isInterrupted())
return;
float[][] spectrum = scans[s].getSpectrum();
if (_process && centroided)
spectrum = FeatureStrategyCentroided.cleanSpectrum(spectrum);
float[] signal = Spectrum.Resample(spectrum, mzWindow, 36);
spectra[s] = signal;
}
if (_process)
{
Spectrum.RemoveBackground(spectra);
if (me.isInterrupted())
return;
}
for (int s = 0; s < scans.length; s++)
{
float[] signal = spectra[s];
float base = mzWindow.min - newImage._mzRange.min;
for (int i = 0; i < signal.length; i++)
{
float mzOff = base + i / 36F;
float in = signal[i];
if (mzOff < 0 || in == 0) continue;
x.add((float)s);
y.add(mzOff);
z.add(in);
}
}
IntensityPlot plot = new IntensityPlot();
String scheme = MSDetailPanel.getColorScheme();
plot.setData(x, y, z);
if ("sqrt".equals(_scale))
plot.plotSqrt(img, _lowThreshold, newImage._yScale, scheme);
else
plot.plotLog(img, _lowThreshold, newImage._yScale, scheme);
g.setColor(Color.BLACK);
for (int w = (int)rect.getWidth(), m = (int)newImage._mzRange.min; m <= newImage._mzRange.max; m++)
{
int t = height - 1 - (m - (int)newImage._mzRange.min) * newImage._yScale;
if (0 == (m % 10))
{
//g.drawLine(w - 2, t + 1, w, t + 1);
//g.drawLine(w - 6, t, w, t);
//g.drawLine(w - 2, t - 1, w, t - 1);
int stringWidth = g.getFontMetrics().stringWidth(String.valueOf(m));
g.drawLine(w - 2, t, w, t);
g.drawString(String.valueOf(m), w - stringWidth - 2, t + 3);
}
else if (0 == (m % 5))
{
g.drawLine(w - 2, t + 1, w, t + 1);
g.drawLine(w - 5, t, w, t);
g.drawLine(w - 2, t - 1, w, t - 1);
}
else
g.drawLine(w - 3, t, w, t);
}
if (me.isInterrupted())
return;
final ImageParameters p = (ImageParameters)newImage.clone();
final BufferedImage i = img;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
_extractedFeatures = null;
_featuresVisible = null;
ApplicationContext.setProperty(SharedProperties.ZOOM_REGION, p._rectZoom);
setImage(i, p);
}
});
}
private void _findDetailFeatures(ImageParameters newImage)
{
Thread me = Thread.currentThread();
MSRun.MSScan[] scans;
java.util.List featureSets = new java.util.LinkedList();
FeatureSet extractFeatures = null; // the _main_ features
int scanMax = Math.min(newImage._scanMin + newImage._scanCount, _run.getScanCount());
// Show information related to the feature extraction algorithm
long msStart = 0; // don't want to time finding background features
try
{
ApplicationContext.setMessage("Looking for features. . .");
int t = scanMax - newImage._scanMin + 32;
int len = 128;
for (; len < t; len = len * 2)
;
int start = Math.max(0, _scanIndex - len / 2);
int end = Math.min(_run.getScanCount(), start + len);
len = end - start;
scans = new MSRun.MSScan[len];
for (int s = 0; s < len; s++)
scans[s] = _run.getScan(start + s);
Class c = (Class)ApplicationContext.getProperty(FeatureExtractor.DEFAULT_EXTRACTOR_PROPERTYNAME);
if (-1 != c.getName().toLowerCase().indexOf("wavelet2d"))
{
FeatureSet s;
Spectrum.Peak[] peaks = ExtractMaxima2D.analyze(scans,
new FloatRange(newImage._mzRange.min - 0.1F, newImage._mzRange.max + 0.1F),
new FeatureStrategyWavelet2D.SmoothWavelet(),
-Float.MAX_VALUE);
if (me.isInterrupted())
return;
s = new FeatureSet(peaks, new Color(0, 0, 0, 0x7f));
s.setStyle(2); // pixel
featureSets.add(s);
}
else if (-1 != c.getName().toLowerCase().indexOf("combined")) // || -1 != c.getTextCode().toLowerCase().indexOf("clusters"))
{
try
{
FeatureSet s = new FeatureStrategyPeaks(_run, newImage._scanMin, newImage._scanCount, 6, new FloatRange(newImage._mzRange.min - 10F, newImage._mzRange.max + 10F), 4.0).analyze();
s.setColor(new Color(0, 0, 0, 0x40));
s.setStyle(2); // pixel
featureSets.add(s);
}
catch (InterruptedException xx)
{
return;
}
}
else if (-1 != c.getName().toLowerCase().indexOf("gross"))
{
msStart = System.currentTimeMillis(); // HACK: we're finding background and foreground features in parallel
FeatureSet[] s = ExtractEdgeFeatures.EdgeFeatures(scans, new FloatRange(newImage._mzRange.min - 10F, newImage._mzRange.max + 10F), 0.0F);
if (me.isInterrupted())
return;
s[0].setStyle(2); // pixel
s[1].setStyle(2); // pixel
featureSets.add(s[0]);
featureSets.add(s[1]);
if (-1 != c.getName().toLowerCase().indexOf("gross"))
{
// don't waste time using FeatureExtractor, we've done the work already
featureSets.add(s[2]);
extractFeatures = s[2];
extractFeatures.setStyle(1);
extractFeatures.setColor(Color.BLACK);
}
}
else if (-1 != c.getName().toLowerCase().indexOf("smooth"))
{
Spectrum.Peak[] peaks = ExtractMaxima2D.analyze(scans, new FloatRange(newImage._mzRange.min - 10F, newImage._mzRange.max + 10F));
if (me.isInterrupted())
return;
FeatureSet s = new FeatureSet(peaks, Color.BLACK);
s.setStyle(2); // pixel
featureSets.add(s);
}
if (me.isInterrupted())
return;
// features on selected scan(s)
if (null == extractFeatures)
{
if (0 == msStart)
msStart = System.currentTimeMillis();
FloatRange mzRangeExtract = new FloatRange(newImage._mzRange.min - 10.0F, newImage._mzRange.max + 10.0F);
Class featureStrategyClass = FeatureExtractor.getDefaultClass();
String featureStrategyPackageName = null;
//TODO: this should NOT be necessary. But something about the way one-jar loads
//the new-school classes is proving a problem.
try
{
featureStrategyPackageName = featureStrategyClass.getPackage().getName();
}
catch (NullPointerException e)
{
String className = featureStrategyClass.getName();
featureStrategyPackageName = className.substring(0, className.lastIndexOf("."));
}
//determine whether this is an old-school feature strategy and invoke appropriately
if ("org.fhcrc.cpl.viewer.feature".equals(featureStrategyPackageName))
{
FeatureExtractor alg = FeatureExtractor.getDefault(_run, newImage._scanMin, newImage._scanCount, 6, mzRangeExtract, 2.0);
// UNDONE: need dialog to set maxCharge
if (FeatureExtractor.TYPE_1D == alg.getType())
alg = FeatureExtractor.getDefault(_run, _scanIndex, 1, 6, mzRangeExtract, 2.0);
extractFeatures = alg.analyze();
}
else
{
FeatureFinder featureFinder =
new FeatureFinder(_run, newImage._scanMin, newImage._scanCount,
PeakCombiner.DEFAULT_MAX_CHARGE,
mzRangeExtract,
featureStrategyClass, false);
featureFinder.setStatusListener(new BaseFeatureStrategy.StatusListener()
{
public void progress(float percent)
{
float p = Math.round(percent * 10) / 10F;
ApplicationContext.setMessage(TextProvider.getText("FINDING_ALL_FEATURES_IN_FILE","FILEPATH",_run.getFile().getName()) +
TextProvider.getText("PERCENT_PERCENT_COMPLETE_DOT","PERCENT",String.valueOf(p)));
}
});
extractFeatures = featureFinder.findPeptides();
}
}
if (extractFeatures != null)
{
extractFeatures.setColor(Color.BLACK);
if (me.isInterrupted())
return;
featureSets.add(extractFeatures);
// if (-1 != alg.getClass().getTextCode().indexOf("FeatureStrategyPeaks"))
// extractFeatures.setStyle(2); // pixels
// else
extractFeatures.setStyle(4); // 1=X 4=box
}
if (me.isInterrupted())
return;
final java.util.List sets = featureSets;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
_extractedFeatures = sets;
repaint();
}
});
}
catch (InterruptedException ex)
{
return;
}
finally
{
long msEnd = System.currentTimeMillis();
if (null == extractFeatures) // interrupted probably
ApplicationContext.setMessage(" ");
else
{
FeatureSet.FeatureSelector sel = getSelector(_run, newImage);
int c = extractFeatures.getFeatures(sel).length;
String time = NumberFormat.getInstance().format((msEnd - msStart) / 1000.0);
ApplicationContext.setMessage("Found " + c + " feature" + (c == 1 ? "" : "s") + " in " + time + "s");
}
}
}
}
static Color TRANSLUCENT_RED = new Color(1.0F, 0.0F, 0.0F, 0.4F);
static Color TRANSLUCENT_BLUE = new Color(0.0F, 0.0F, 1.0F, 0.6F);
public void paint(Graphics g)
{
_log.debug("MSDetailPanel.paint()");
super.paint(g);
if (null == this._curr)
return;
Graphics2D graphics = (Graphics2D)g;
//
// draw SpectrumComponent range
//
Pair chartRange = (Pair)ApplicationContext.getProperty(SharedProperties.CHART_RANGE);
if (getShowChartRange() && null != chartRange)
{
int height = getHeight();
Spectrum.Peak start = (Spectrum.Peak)chartRange.first;
Spectrum.Peak end = (Spectrum.Peak)chartRange.second;
MSRun run = (MSRun)ApplicationContext.getProperty(SharedProperties.MS_RUN);
if (null == run || null == _curr)
return;
int xStart = run.getIndexForScanNum(start.scan) - _curr._scanMin;
int xEnd = run.getIndexForScanNum(end.scan) - _curr._scanMin;
int yStart = (int)(height - 1 - (start.mz - _curr._mzRange.min) * _curr._yScale);
int yEnd = (int)(height - 1 - (end.mz - _curr._mzRange.min) * _curr._yScale);
if ("fire".equals(getColorScheme().toLowerCase()) || "rainbow".equals(getColorScheme().toLowerCase()))
graphics.setColor(TRANSLUCENT_BLUE);
else
graphics.setColor(TRANSLUCENT_RED);
graphics.drawLine(xStart, yStart, xEnd, yEnd);
}
//
// draw features
//
// for scale get TIC of selected scan,
// CONSIDER use median TIC of whole file so it doesn't change
float ticScale = 1;
try
{
MSRun run = (MSRun)ApplicationContext.getProperty(SharedProperties.MS_RUN);
int scanMin = _curr._scanMin;
float[] tic = new float[_curr._scanCount];
int s = 0;
for (s = 0; s < _curr._scanCount && scanMin + s < run.getScanCount(); s++)
tic[s] = run.getScan(scanMin + s).getTotIonCurrent();
float med = Spectrum.Median(tic, 0, s, false, null);
ticScale = Math.max(ticScale, med / 200000F);
}
catch (Throwable t)
{
}
// only need to recompute if features change, but this is convienent
_featuresVisible = new Tree2D();
MSRun run = (MSRun)ApplicationContext.getProperty(SharedProperties.MS_RUN);
if (null == run)
return;
ArrayList featureSetsApp = (ArrayList)ApplicationContext.getProperty(SharedProperties.FEATURE_RANGES);
ArrayList featureSets = new ArrayList();
if (null != featureSetsApp)
featureSets.addAll(featureSetsApp);
if (null != _extractedFeatures)
featureSets.addAll(_extractedFeatures);
for (int i = 0; i < featureSets.size(); i++)
{
FeatureSet fs = (FeatureSet)featureSets.get(i);
if (!fs.isDisplayed())
continue;
Color color = fs.getColor();
Color light = new Color(color.getRed(), color.getGreen(), color.getBlue(), 0x40);
int height = getHeight();
float mzMin = _curr._mzRange.min;
FeatureSet.FeatureSelector selector = getSelector(run, _curr);
Feature[] features = fs.getFeatures(selector);
graphics.setColor(color);
for (int j = 0; j < features.length; j++)
{
Feature f = features[j];
// UNDONE: convert to retention time
int index = run.getIndexForScanNum(f.scan, true);
if (fs.getStyle() != 2) // don't hit test the "pixel" features
_featuresVisible.add(index, f.mz, f);
int yDraw = (int)(height - 1 - (f.mz - mzMin) * _curr._yScale);
float mzTop = f.mz + Math.max(0, f.peaks - 1) / Math.max(1F, f.charge);
int yTop = (int)(height - 1 - (mzTop - mzMin) * _curr._yScale);
int xDraw = index - _curr._scanMin;
int leftMargin = 2;
int rightMargin = 2;
if (f instanceof Feature)
{
Feature fr = (Feature)f;
int indexStart = run.getIndexForScanNum(fr.getScanFirst(), true);
int indexLast = run.getIndexForScanNum(fr.getScanLast(), true);
leftMargin = fr.getScanFirst() == 0 ? 2 : Math.min(40, Math.max(2, index - indexStart));
rightMargin = fr.getScanLast() == 0 ? 2 : Math.min(40, Math.max(2, indexLast - index));
}
if (fs.getStyle() == 1 || fs.getStyle() == 4) // X
{
int l = (int)Math.log(f.intensity / ticScale + 6);
g.drawLine(xDraw - l, yDraw - l, xDraw + l, yDraw + l);
g.drawLine(xDraw - l, yDraw + l, xDraw + l, yDraw - l);
}
else if (fs.getStyle() == 2) // pixel
{
g.drawLine(xDraw, yDraw, xDraw, yDraw);
}
else // +
{
if (null != IsotopicLabelExtraInfoDef.getLabel(f) &&
0 < IsotopicLabelExtraInfoDef.getLabelCount(f))
{
float mzHeavy =
f.mz +
(IsotopicLabelExtraInfoDef.getLabelCount(f) *
IsotopicLabelExtraInfoDef.getLabel(f).getHeavy()
/ f.charge);
int y2 = (int)(height - 1 - (mzHeavy - mzMin) * _curr._yScale);
graphics.drawLine(xDraw - 2, yDraw, xDraw + 2, yDraw);
graphics.drawLine(xDraw - 1, yDraw - 1, xDraw + 1, yDraw - 1);
graphics.drawLine(xDraw - 2, y2, xDraw + 2, y2);
graphics.drawLine(xDraw - 1, y2 + 1, xDraw + 1, y2 + 1);
graphics.drawLine(xDraw, yDraw, xDraw, y2);
}
else
{
g.drawLine(xDraw - 2, yDraw, xDraw + 2, yDraw);
g.drawLine(xDraw, yDraw - 1, xDraw, yDraw + 1);
}
}
if (fs.getStyle() == 4 && f instanceof Feature)
{
Feature fr = (Feature)f;
if (fr.peaks < 2 && fr.scanFirst == fr.scanLast)
continue;
int bottom = yDraw + 2;
int top = yTop - 2;
if (fr.peaks < 2)
g.drawLine(xDraw - leftMargin, yDraw, xDraw + rightMargin, yDraw);
else if (fr.scanFirst == fr.scanLast)
g.drawLine(xDraw, top - 1, xDraw, bottom + 1);
else
{
g.setColor(light);
g.drawOval(xDraw - leftMargin - 1, top - 1, leftMargin + rightMargin + 2, bottom - top + 2);
g.setColor(color);
}
}
}
}
}
private FeatureSet.FeatureSelector getSelector(MSRun run, ImageParameters img)
{
float mzMin = img._mzRange.min;
float mzMax = mzMin + img._rectZoom.height;
FeatureSet.FeatureSelector selector = (FeatureSet.FeatureSelector)ApplicationContext.getProperty("featureSelector");
if (null == selector)
selector = new FeatureSet.FeatureSelector();
else
selector = (FeatureSet.FeatureSelector)selector.clone();
selector.setMinMz(mzMin - 30); // need a pretty wide range here because of icat
selector.setMaxMz(mzMax + 1);
// map scanIndex to scanNum
selector.setScanFirst(run.getScan(Math.max(0, img._scanMin - 10)).getNum());
selector.setScanLast(run.getScan(Math.min(run.getScanCount() - 1, img._scanMin + img._scanCount + 10)).getNum());
return selector;
}
// CONSIDER: just use statics instead of application properties
static int getLowThreshold()
{
try
{
String t = (String)ApplicationContext.getProperty(LOW_THRESHOLD);
return Integer.parseInt(t);
}
catch (Exception x)
{
return 0;
}
}
static void setLowThreshold(int low)
{
ApplicationContext.setProperty(LOW_THRESHOLD, "" + low);
}
static void setScale(String scale)
{
ApplicationContext.setProperty(SCALE, scale);
}
static void setColorScheme(String scheme)
{
ApplicationContext.setProperty(COLORSCHEME, scheme);
}
static void setPreprocess(boolean r)
{
ApplicationContext.setProperty(PREPROCESS, r ? Boolean.TRUE : Boolean.FALSE);
}
static boolean getPreprocess()
{
try
{
Boolean bool = (Boolean)ApplicationContext.getProperty(MSDetailPanel.PREPROCESS);
return null == bool ? false : bool.booleanValue();
}
catch (Exception x)
{
}
return false;
}
static void setFindFeatures(boolean r)
{
ApplicationContext.setProperty(FINDFEATURES, r ? Boolean.TRUE : Boolean.FALSE);
}
static boolean getFindFeatures()
{
try
{
Boolean bool = (Boolean)ApplicationContext.getProperty(MSDetailPanel.FINDFEATURES);
return null == bool ? true : bool.booleanValue();
}
catch (Exception x)
{
}
return true;
}
static void setShowChartRange(boolean r)
{
ApplicationContext.setProperty(SHOWCHARTLINE, r ? Boolean.TRUE : Boolean.FALSE);
}
static boolean getShowChartRange()
{
try
{
Boolean bool = (Boolean)ApplicationContext.getProperty(MSDetailPanel.SHOWCHARTLINE);
return null == bool ? true : bool.booleanValue();
}
catch (Exception x)
{
}
return true;
}
static String getScale()
{
String scale = null;
try
{
scale = (String)ApplicationContext.getProperty(SCALE);
}
catch (Exception x)
{
}
return null == scale ? "log" : scale;
}
static String getColorScheme()
{
String s = null;
try
{
s = (String)ApplicationContext.getProperty(COLORSCHEME);
}
catch (Exception x)
{
}
return null == s ? "Cyan" : s;
}
public static class ShowDialogAction extends AbstractAction
{
ShowDialogAction()
{
super("Detail Pane Settings");
}
public void actionPerformed(ActionEvent e)
{
new DetailDialog().setVisible(true);
}
}
public static class Show3DAction extends AbstractAction
{
Show3DAction()
{
super("3D view of Detail Pane");
}
public void actionPerformed(ActionEvent e)
{
Rectangle rect = (Rectangle)ApplicationContext.getProperty(SharedProperties.ZOOM_REGION);
MSRun run = (MSRun)ApplicationContext.getProperty(SharedProperties.MS_RUN);
if (null == rect || null == run)
return;
ApplicationContext.setMessage(rect.toString());
float[][] matrix = new float[rect.width][];
FloatRange r = new FloatRange(rect.y, rect.y + rect.height);
int start = Math.max(0, rect.x);
int end = Math.min(run.getScanCount() - 1, rect.x + rect.width);
for (int s = start; s < end; s++)
{
Scan scan = run.getScan(s);
matrix[s - start] = Spectrum.Resample(scan.getSpectrum(), r, 36);
}
JFrame frame = SurfaceFrame.ShowSurfaceFrame(matrix);
frame.setVisible(true);
}
}
public static class DetailDialog extends JDialog
{
public JSpinner lowSpinner;
public JButton okButton;
public JButton cancelButton;
public JButton applyButton;
public JPanel contentPanel;
public JRadioButton radioLogScale;
public JRadioButton radioSqrtScale;
public JCheckBox checkPreprocess;
public JCheckBox checkDetectFeatures;
public JComboBox heatmapColorScheme;
public JCheckBox checkShowChartRange;
DetailDialog()
{
super(ApplicationContext.getFrame(), "Detail Pane Settings", false);
try
{
new SwingEngine(this).render("org/fhcrc/cpl/viewer/gui/DetailDialog.xml");
assert null != contentPanel;
}
catch (Exception x)
{
ApplicationContext.errorMessage("error creating dialog", x);
throw new RuntimeException(x);
}
this.setContentPane(contentPanel);
ButtonGroup group = new ButtonGroup();
group.add(radioSqrtScale);
group.add(radioLogScale);
String[] s = IntensityPlot.COLOR_SCHEMES;
for (int j = 0; j < s.length; j++) heatmapColorScheme.addItem(s[j]);
lowSpinner.setValue(new Integer(getLowThreshold()));
((SpinnerNumberModel)lowSpinner.getModel()).setMinimum(new Integer(0));
if ("sqrt".equals(getScale()))
radioSqrtScale.setSelected(true);
else
radioLogScale.setSelected(true);
heatmapColorScheme.setSelectedItem(getColorScheme());
checkPreprocess.setSelected(getPreprocess());
checkDetectFeatures.setSelected(getFindFeatures());
checkShowChartRange.setSelected(getShowChartRange());
ListenerHelper helper = new ListenerHelper(this);
helper.addListener(cancelButton,"cancel_actionPerformed");
helper.addListener(okButton,"ok_actionPerformed");
helper.addListener(applyButton,"apply_actionPerformed");
this.setSize(300, 220);
doLayout();
}
public void ok_actionPerformed(ActionEvent e)
{
apply_actionPerformed(null);
this.dispose();
}
public void cancel_actionPerformed(ActionEvent e)
{
this.dispose();
}
public void apply_actionPerformed(ActionEvent e)
{
if ((lowSpinner.getValue() instanceof Integer))
{
Integer I = (Integer)lowSpinner.getValue();
int low = Math.max(0, I.intValue());
MSDetailPanel.setLowThreshold(low);
}
MSDetailPanel.setScale(radioSqrtScale.isSelected() ? "sqrt" : "log");
MSDetailPanel.setColorScheme((String)heatmapColorScheme.getSelectedItem());
MSDetailPanel.setPreprocess(checkPreprocess.isSelected());
MSDetailPanel.setFindFeatures(checkDetectFeatures.isSelected());
MSDetailPanel.setShowChartRange(checkShowChartRange.isSelected());
}
}
}