/******************************************************************************* * 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.core.manager.project; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import edu.yu.einstein.genplay.dataStructure.genomeWindow.GenomeWindow; import edu.yu.einstein.genplay.gui.event.genomeWindowEvent.GenomeWindowEvent; import edu.yu.einstein.genplay.gui.event.genomeWindowEvent.GenomeWindowEventsGenerator; import edu.yu.einstein.genplay.gui.event.genomeWindowEvent.GenomeWindowListener; /** * This class manages the genome window displayed, the track current width and the ratio between them. * {@link GenomeWindowListener} can be registered into this class to receive notification about window changes. * @author Nicolas Fourel * @author Julien Lajugie * @version 0.1 */ public class ProjectWindow implements Serializable, GenomeWindowEventsGenerator { private static final long serialVersionUID = -9014173267531950797L; // Generated serial version ID private static final int SAVED_FORMAT_VERSION_NUMBER = 0; // saved format version private List<GenomeWindowListener> gwListenerList; // list of GenomeWindowListener private GenomeWindow genomeWindow; // the genome window displayed by the track private int trackWidth; // width of the tracks private transient double xRatio; // ratio of the track width to the genome window width /** * Constructor of {@link ProjectWindow} */ protected ProjectWindow () { gwListenerList = new ArrayList<GenomeWindowListener>(); genomeWindow = null; trackWidth = 0; xRatio = 0; } @Override public void addGenomeWindowListener(GenomeWindowListener genomeWindowListener) { if (!gwListenerList.contains(genomeWindowListener)) { gwListenerList.add(genomeWindowListener); } } /** * @param doubleValue a double value * @return the largest integer value that is less than or equal to the argument specified double value * Returns Integer.MAX_VALUE if the specified double is greater than Integer.MAX_VALUE * Returns Integer.MIN_VALUE if the specified double is smaller than Integer.MIN_VALUE */ private int doubleToIntFloor(double doubleValue) { if (doubleValue >= Integer.MAX_VALUE) { return Integer.MAX_VALUE; } else if (doubleValue <= Integer.MIN_VALUE) { return Integer.MIN_VALUE; } else { return (int)Math.floor(doubleValue); } } /** * @param doubleValue a double value * @return the closest integer value to the specified double value * Returns Integer.MAX_VALUE if the specified double is greater than Integer.MAX_VALUE * Returns Integer.MIN_VALUE if the specified double is smaller than Integer.MIN_VALUE */ private int doubleToIntRounded(double doubleValue) { if (doubleValue >= Integer.MAX_VALUE) { return Integer.MAX_VALUE; } else if (doubleValue <= Integer.MIN_VALUE) { return Integer.MIN_VALUE; } else { return (int) Math.round(doubleValue); } } /** * @param position a position on a chromosome in base pair * @return a position on the screen */ public long genomeToAbsoluteScreenPosition(int position) { return Math.round(position * xRatio); } /** * @param genomePosition a position on the genome in bp * @return a horizontal position in pixel corresponding to the specified genomic position (can be out of the track bounds) */ public int genomeToScreenPosition(int genomePosition) { double result = (genomePosition - genomeWindow.getStart()) * xRatio; return doubleToIntRounded(result); } /** * @param genomeWidth a genomic width in bp * @return a width in pixels corresponding to the specified genomic width */ public int genomeToScreenWidth(int genomeWidth) { double result = genomeWidth * xRatio; return doubleToIntRounded(result); } /** * @return the genomeWindow */ public GenomeWindow getGenomeWindow() { return genomeWindow; } @Override public GenomeWindowListener[] getGenomeWindowListeners() { GenomeWindowListener[] genomeWindowListeners = new GenomeWindowListener[gwListenerList.size()]; return gwListenerList.toArray(genomeWindowListeners); } /** * @return the width of the tracks in pixel (all tracks have the same width) */ public int getTrackWidth() { return trackWidth; } /** * @return the ratio of the track width to the genome window width */ public double getXRatio() { return xRatio; } /** * Show {@link ProjectWindow} content */ public void print() { String info = ""; info += "Factor: " + xRatio + "\n"; info += "Window: " + genomeWindow.getStart() + " to " + genomeWindow.getStop() + ", size: " + genomeWindow.getSize() + "\n"; info += "Factor / Window size: " + (xRatio / genomeWindow.getSize()) + "\n"; info += "Window size / Factor: " + (genomeWindow.getSize() / xRatio); System.out.println(info); } /** * Method used for unserialization * @param in * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.readInt(); setGenomeWindow((GenomeWindow) in.readObject()); setTrackWidth(in.readInt()); updateXRatio(); gwListenerList = new ArrayList<GenomeWindowListener>(); } /** * Removes all registered {@link GenomeWindowListener} */ public void removeAllListeners () { gwListenerList.clear(); } @Override public void removeGenomeWindowListener(GenomeWindowListener genomeWindowListener) { gwListenerList.remove(genomeWindowListener); } /** * @param xScreen horizontal position on the screen (on the track) in pixel * @return position on the genome in bp */ public int screenToGenomePosition(int xScreen) { double result = Math.floor(xScreen / xRatio); // the value must be rounded the lowest int. Position 10,5 is still 10, not 11. result += genomeWindow.getStart(); return doubleToIntFloor(result); } /** * @param screenWidth a width on the screen in pixels * @return the distance in bp corresponding to the specified screen distance */ public int screenToGenomeWidth(int screenWidth) { double result = screenWidth / xRatio; return doubleToIntFloor(result); } /** * Sets the genome window value. If the new value is different from the current one * the {@link GenomeWindowListener} will be notified * @param genomeWindow {@link GenomeWindow} to set */ public void setGenomeWindow(GenomeWindow genomeWindow) { if (!genomeWindow.equals(this.genomeWindow)) { GenomeWindow oldGenomeWindow = this.genomeWindow; this.genomeWindow = genomeWindow; // update the xRatio updateXRatio(); // we notify the listeners GenomeWindowEvent evt = new GenomeWindowEvent(this, oldGenomeWindow, genomeWindow); if ((gwListenerList != null) && !gwListenerList.isEmpty()) { for (GenomeWindowListener currentListener: gwListenerList) { currentListener.genomeWindowChanged(evt); } } } } /** * Sets this instance fields with the values of the specified {@link ProjectWindow} * @param projectWindow */ public void setProjectWindow(ProjectWindow projectWindow) { setGenomeWindow(projectWindow.getGenomeWindow()); setTrackWidth(projectWindow.getTrackWidth()); updateXRatio(); } /** * Sets the width of the tracks in pixel * @param trackWidth width of the track to set */ public void setTrackWidth(int trackWidth) { if (this.trackWidth != trackWidth) { this.trackWidth = trackWidth; // update the xRatio updateXRatio(); } } /** * Computes and sets the xRatio variable. * The xRatio is defines as the ratio of the track width to the genome window width */ private void updateXRatio() { if (genomeWindow.getSize() == 0) { xRatio = 0; } else { xRatio = trackWidth / (double)(genomeWindow.getSize()); } } /** * Method used for serialization * @param out * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(SAVED_FORMAT_VERSION_NUMBER); out.writeObject(getGenomeWindow()); out.writeInt(getTrackWidth()); } }