/*******************************************************************************
* GenPlay, Einstein Genome Analyzer
* Copyright (C) 2009, 2014 Albert Einstein College of Medicine
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu>
* Nicolas Fourel <nicolas.fourel@einstein.yu.edu>
* Eric Bouhassira <eric.bouhassira@einstein.yu.edu>
*
* Website: <http://genplay.einstein.yu.edu>
******************************************************************************/
package edu.yu.einstein.genplay.gui.track.layer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import edu.yu.einstein.genplay.core.manager.project.ProjectManager;
import edu.yu.einstein.genplay.core.manager.project.ProjectWindow;
import edu.yu.einstein.genplay.core.operation.SCWList.SCWLOMaxScoreToDisplay;
import edu.yu.einstein.genplay.core.operation.SCWList.SCWLOMinScoreToDisplay;
import edu.yu.einstein.genplay.dataStructure.chromosome.Chromosome;
import edu.yu.einstein.genplay.dataStructure.enums.GraphType;
import edu.yu.einstein.genplay.dataStructure.genomeWindow.GenomeWindow;
import edu.yu.einstein.genplay.dataStructure.list.genomeWideList.SCWList.SCWList;
import edu.yu.einstein.genplay.dataStructure.list.listView.ListView;
import edu.yu.einstein.genplay.dataStructure.scoredChromosomeWindow.ScoredChromosomeWindow;
import edu.yu.einstein.genplay.gui.dataScalerForTrackDisplay.DataScalerManager;
import edu.yu.einstein.genplay.gui.track.Track;
import edu.yu.einstein.genplay.gui.track.TrackConstants;
import edu.yu.einstein.genplay.util.colors.Colors;
import edu.yu.einstein.genplay.util.colors.LayerColors;
/**
* Abstract class that defining the common methods of {@link BinLayer} and {@link SimpleSCWLayer} objects.
* @author Julien Lajugie
* @param <T> type of the data showed in the layer. The type T must implement {@link SCWList}.
*/
public abstract class AbstractSCWLayer<T extends SCWList> extends AbstractVersionedLayer<T> implements Cloneable, Layer<T>, VersionedLayer<T>, GraphLayer, ColoredLayer {
/** Generated serial ID */
private static final long serialVersionUID = 1612912685829758955L;
/** Version number of the class */
private static final transient int CLASS_VERSION_NUMBER = 0;
/** Type of graph display in the layer */
private GraphType graphType;
/** Color of the layer */
private Color color;
/** Reversed of the curve color, use to print negative values */
transient private Color reversedColor;
/**
* Creates an instance of {@link AbstractSCWLayer} with the same properties as the specified {@link AbstractSCWLayer}
* @param abstractLayer
*/
protected AbstractSCWLayer(AbstractSCWLayer<T> abstractSCWLayer) {
super(abstractSCWLayer);
this.graphType = abstractSCWLayer.graphType;
this.color = abstractSCWLayer.color;
this.reversedColor = Colors.getReversedColor(color);
}
/**
* Creates an instance of a {@link SimpleSCWLayer}
* @param track track containing the layer
* @param data data of the layer
* @param name name of the layer
*/
public AbstractSCWLayer(Track track, T data, String name) {
super(track, data, name);
this.graphType = TrackConstants.DEFAULT_GRAPH_TYPE;
this.color = LayerColors.getLayerColor();
this.reversedColor = Colors.getReversedColor(color);
}
@Override
public abstract AbstractSCWLayer<T> clone();
@Override
public void draw(Graphics g, int width, int height) {
if (isVisible()) {
Graphics2D g2D = (Graphics2D)g;
switch(getGraphType()) {
case BAR:
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
drawBarGraph(g, width, height);
break;
case CURVE:
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
drawCurveGraph(g, width, height);
break;
case POINTS:
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
drawPointGraph(g, width, height);
break;
case DENSE:
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
drawDenseGraph(g, width, height);
break;
}
}
}
/**
* Draws a bar on the specified graphics object with the specified coordinate
* @param g graphics where to draw the bar
* @param x x position of the bar
* @param screenY0 y0 position on the screen
* @param width width of the window
* @param score score of the bar
*/
private void drawBar(Graphics g, int x, int screenY0, int width, float score) {
int y = getTrack().getScore().scoreToScreenPosition(score);
int rectHeight = y - screenY0;
if (score > 0) {
g.setColor(color);
g.fillRect(x, y, width, -rectHeight);
} else {
g.setColor(reversedColor);
g.fillRect(x, screenY0, width, rectHeight);
}
}
/**
* Draws the layer as a bar graph
* @param g {@link Graphics} on which the layer will be drawn
* @param width width of the graphics to draw
* @param height height of the graphics to draw
*/
private void drawBarGraph(Graphics g, int width, int height) {
if (getData() != null) {
ProjectWindow projectWindow = ProjectManager.getInstance().getProjectWindow();
int screenY0 = getTrack().getScore().scoreToScreenPosition(0);
// Retrieve the list to print
ListView<ScoredChromosomeWindow> listToPrint = DataScalerManager.getInstance().getScaledData(this);
if (listToPrint == null) {
getTrack().drawLoadingAnimation(g);
} else {
Integer waitingX = null;
Float waitingScore = null;
for (ScoredChromosomeWindow currentWindow: listToPrint) {
float score = currentWindow.getScore();
if (score != 0) {
// we want to make sure that x is > 0
int x = Math.max(0, projectWindow.genomeToScreenPosition(currentWindow.getStart()));
// if the width of the precedent window was 0 we print it with a width of 1
// if it doesn't overlap with the current window.
if ((waitingX != null) && (waitingX < x)) {
drawBar(g, waitingX, screenY0, 1, waitingScore);
}
waitingX = null;
waitingScore = null;
// we want to make sure that window width is not larger than the screen width
int widthWindow = Math.min(width, projectWindow.genomeToScreenPosition(currentWindow.getStop()) - x);
if (widthWindow > 0) {
drawBar(g, x, screenY0, widthWindow, score);
} else {
// if the width is less than 1 we save it for the next iteration in our list of windows to print
// because we will print it with a width of 1 if it doesn't overlap with the next window
waitingX = x;
waitingScore = score;
}
}
}
if (waitingX != null) {
drawBar(g, waitingX, screenY0, 1, waitingScore);
}
}
}
}
/**
* Draws the layer as a curve graph
* @param g {@link Graphics} on which the layer will be drawn
* @param width width of the graphics to draw
* @param height height of the graphics to draw
*/
private void drawCurveGraph(Graphics g, int width, int height) {
if (getData() != null) {
ProjectWindow projectWindow = ProjectManager.getInstance().getProjectWindow();
g.setColor(getColor());
// Retrieve the list to print
ListView<ScoredChromosomeWindow> listToPrint = DataScalerManager.getInstance().getScaledData(this);
if (listToPrint == null) {
getTrack().drawLoadingAnimation(g);
} else {
int x1 = -1;
int x2 = -1;
float score1 = Float.NaN;
int y1 = -1;
float score2 = Float.NaN;
int y2 = -1;
for (ScoredChromosomeWindow currentWindow: listToPrint) {
x2 = projectWindow.genomeToScreenPosition(currentWindow.getStart());
score2 = currentWindow.getScore();
y2 = getTrack().getScore().scoreToScreenPosition(score2);
if (x1 != -1) {
if ((score1 == 0) && (score2 != 0)) {
g.drawLine(x2, y1, x2, y2);
} else if ((score1 != 0) && (score2 == 0)) {
g.drawLine(x1, y1, x2, y1);
g.drawLine(x2, y1, x2, y2);
} else if ((score1 != 0) && (score2 != 0)) {
g.drawLine(x1, y1, x2, y2);
}
}
x1 = x2;
score1 = score2;
y1 = y2;
}
if (x1 != -1) {
if ((score1 == 0) && (score2 != 0)) {
g.drawLine(x2, y1, x2, y2);
} else if ((score1 != 0) && (score2 == 0)) {
g.drawLine(x1, y1, x2, y1);
g.drawLine(x2, y1, x2, y2);
} else if ((score1 != 0) && (score2 != 0)) {
g.drawLine(x1, y1, x2, y2);
}
}
}
}
}
/**
* Draws the layer as a dense graph
* @param g {@link Graphics} on which the layer will be drawn
* @param width width of the graphics to draw
* @param height height of the graphics to draw
*/
private void drawDenseGraph(Graphics g, int width, int height) {
if (getData() != null) {
ProjectWindow projectWindow = ProjectManager.getInstance().getProjectWindow();
// Retrieve the list to print
ListView<ScoredChromosomeWindow> listToPrint = DataScalerManager.getInstance().getScaledData(this);
if (listToPrint == null) {
getTrack().drawLoadingAnimation(g);
} else {
for (ScoredChromosomeWindow currentWindow: listToPrint) {
int x = projectWindow.genomeToScreenPosition(currentWindow.getStart());
int widthWindow = projectWindow.genomeToScreenPosition(currentWindow.getStop()) - x;
// we want to make sure that the window width is > 0
widthWindow = Math.max(1, widthWindow);
double scoreMin = getTrack().getScore().getMinimumScore();
double scoreMax = getTrack().getScore().getMaximumScore();
g.setColor(Colors.scoreToColor(currentWindow.getScore(), scoreMin, scoreMax));
g.fillRect(x, 0, widthWindow, height);
}
}
}
}
/**
* Draws the layer as a point graph
* @param g {@link Graphics} on which the layer will be drawn
* @param width width of the graphics to draw
* @param height height of the graphics to draw
*/
private void drawPointGraph(Graphics g, int width, int height) {
if (getData() != null) {
ProjectWindow projectWindow = ProjectManager.getInstance().getProjectWindow();
g.setColor(getColor());
ListView<ScoredChromosomeWindow> listToPrint = DataScalerManager.getInstance().getScaledData(this);
if (listToPrint == null) {
getTrack().drawLoadingAnimation(g);
} else {
for (ScoredChromosomeWindow currentWindow: listToPrint) {
// we want to make sure that x is > 0
int x1 = Math.max(0, projectWindow.genomeToScreenPosition(currentWindow.getStart()));
// we want to make sure that window width is not larger than the screen width
int x2 = Math.min(width, projectWindow.genomeToScreenPosition(currentWindow.getStop()));
if ((x2 - x1) < 1) {
x2 = x1 + 1;
}
int y = getTrack().getScore().scoreToScreenPosition(currentWindow.getScore());
g.drawLine(x1, y, x2, y);
}
}
}
}
@Override
public Color getColor() {
return color;
}
@Override
public float getCurrentScoreToDisplay() {
if (getData() != null) {
GenomeWindow displayedWindow = ProjectManager.getInstance().getProjectWindow().getGenomeWindow();
Chromosome chromo = displayedWindow.getChromosome();
int middlePosition = (int) displayedWindow.getMiddlePosition();
return getData().getScore(chromo, middlePosition);
} else {
return 0f;
}
}
@Override
public GraphType getGraphType() {
return graphType;
}
@Override
public float getMaximumScoreToDisplay() {
return new SCWLOMaxScoreToDisplay(getData()).compute();
}
@Override
public float getMinimumScoreToDisplay() {
return new SCWLOMinScoreToDisplay(getData()).compute();
}
/**
* Method used for deserialization
* @param in
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.readInt();
color = (Color) in.readObject();
reversedColor = Colors.getReversedColor(color);
graphType = (GraphType) in.readObject();
}
@Override
public void setColor(Color color) {
this.color = color;
this.reversedColor = Colors.getReversedColor(color);
}
@Override
public <U extends T> void setData(U data) {
super.setData(data);
}
@Override
public <U extends T> void setData(U data, String description) {
super.setData(data, description);
}
@Override
public void setGraphType(GraphType graphType) {
this.graphType = graphType;
}
/**
* Method used for serialization
* @param out
* @throws IOException
*/
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeInt(CLASS_VERSION_NUMBER);
out.writeObject(color);
out.writeObject(graphType);
}
}