/*******************************************************************************
* 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.customComponent.scatterPlot;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.io.Serializable;
import edu.yu.einstein.genplay.dataStructure.enums.LogBase;
import edu.yu.einstein.genplay.util.NumberFormats;
import edu.yu.einstein.genplay.util.Utils;
/**
* Axis of a scatter plot
* @author Julien Lajugie
* @version 0.1
*/
public class ScatterPlotAxis implements Serializable {
private static final long serialVersionUID = 487345102079879893L; // generated ID
private static final int HALF_MAJOR_TICK_SIZE = 4; // half size of the major ticks
private static final int HALF_MINOR_TICK_SIZE = 2; // half size of the minor ticks
private final boolean orientation; // orientation of the axis
private double min; // minimum of the axis
private double max; // maximum of the axis
private double majorUnit; // major unit of the ticks
private double minorUnit; // minor unit of the ticks
private boolean showGrid = true; // set to true to show the grid associated with this axis
private boolean isLogScale = false; // true if the axis is on a log scale
private LogBase logBase = LogBase.BASE_10; // the base of the log scale
private String name; // name of the axis
private int position; // position of the axis (y position for an horizontal axis and x position for a vertical one)
/**
* Horizontal axis
*/
public static final boolean HORIZONTAL = true;
/**
* Vertical axis
*/
public static final boolean VERTICAL = false;
/**
* Creates an instance of a {@link ScatterPlotAxis}
* @param dataMin minimum of the axis
* @param dataMax maximum of the axis
* @param orientation orientation of the axis
* @param name name of the axis
*/
public ScatterPlotAxis(double dataMin, double dataMax, boolean orientation, String name) {
this.orientation = orientation;
this.name = name;
findDefaultMin(dataMin);
findDefaultMax(dataMax);
findDefaultUnits();
}
/**
* Translates a data value to its position on the screen
* @param dataValue a value
* @param clip area where the graph is plotted
* @return the position on the screen on the axis
*/
protected int dataValueToScreenPosition(double dataValue, Rectangle clip) {
double max = this.max;
if (isLogScale) {
if (this.max > 0) {
max = Utils.log(logBase, this.max);
}
if (dataValue <= 0) {
dataValue = 0;
} else {
dataValue = Utils.log(logBase, dataValue);
}
}
double position = 0;
if (orientation == HORIZONTAL) {
int padX = clip.x;
int width = clip.width;
if (min == max) {
position = padX;
} else {
position = (((dataValue - min) * width) / (max - min)) + padX;
}
} else {
int height = clip.height;
int padY = clip.y;
if (min == max) {
position = padY;
} else {
position = (height - (((dataValue - min) * height) / (max - min))) + padY;
}
}
return (int) position;
}
/**
* Draws the axis
* @param g {@link Graphics}
* @param clip rectangle where the chart is printed
*/
protected void drawAxis(Graphics g, Rectangle clip) {
// the axis are black
g.setColor(Color.BLACK);
// draw the axis line
if (orientation == HORIZONTAL) {
g.drawLine(clip.x, position, clip.width + clip.x, position);
} else {
g.drawLine(position, clip.y, position, clip.y + clip.height);
}
}
/**
* Draws the grid if the showGrid option is set to true
* @param g {@link Graphics}
* @param clip rectangle where the chart is printed
*/
protected void drawGrid(Graphics g, Rectangle clip) {
if (showGrid) {
g.setColor(Color.LIGHT_GRAY);
double firstLine = ((int) (min / majorUnit)) * majorUnit;
for (double i = firstLine; i <= max; i += majorUnit) {
int pos = dataValueToScreenPosition(i, clip);
if (orientation == HORIZONTAL) {
g.drawLine(pos, clip.y, pos, clip.y + clip.height);
} else {
g.drawLine(clip.x, pos, clip.width + clip.x, pos);
}
}
}
}
/**
* Draws the major ticks with the numbers
* @param g {@link Graphics}
* @param clip rectangle where the chart is printed
*/
protected void drawMajorUnit(Graphics g, Rectangle clip) {
g.setColor(Color.BLACK);
double firstTick = ((int) (min / majorUnit)) * majorUnit;
Integer lastLabelPosition = null;
// height of the font
int labelHeight = g.getFontMetrics().getHeight();
for (double i = firstTick; i <= max; i += majorUnit) {
int pos = dataValueToScreenPosition(i, clip);
String label = NumberFormats.getScoreFormat().format(i);
int labelWidth = g.getFontMetrics().stringWidth(label);
if (orientation == HORIZONTAL) {
g.drawLine(pos, position - HALF_MAJOR_TICK_SIZE, pos, position + HALF_MAJOR_TICK_SIZE);
// compute the positions of the label
int labelXPosition = pos;
int labelYPosition = clip.y + clip.height + HALF_MAJOR_TICK_SIZE + labelHeight;
// draw the label only if there is enough room
if ((lastLabelPosition == null) || ((lastLabelPosition + labelWidth) < labelXPosition)) {
g.drawString(label, labelXPosition, labelYPosition);
lastLabelPosition = labelXPosition;
}
} else {
g.drawLine(position - HALF_MAJOR_TICK_SIZE, pos, position + HALF_MAJOR_TICK_SIZE, pos);
// compute the positions of the label
int labelXPosition = clip.x - HALF_MAJOR_TICK_SIZE - labelWidth;
int labelYPosition = (int) (pos + (labelHeight / (double) 4));
// draw the label only if there is enough room
if ((lastLabelPosition == null) || ((lastLabelPosition - labelHeight) > labelYPosition)) {
g.drawString(label, labelXPosition, labelYPosition);
lastLabelPosition = labelYPosition;
}
}
}
}
/**
* Draws the minor ticks
* @param g {@link Graphics}
* @param clip rectangle where the chart is printed
*/
protected void drawMinorUnit(Graphics g, Rectangle clip) {
g.setColor(Color.DARK_GRAY);
double firstTick = ((int) (min / minorUnit)) * minorUnit;
for (double i = firstTick; i <= max; i += minorUnit) {
int pos = dataValueToScreenPosition(i, clip);
if (orientation == HORIZONTAL) {
g.drawLine(pos, position - HALF_MINOR_TICK_SIZE, pos, position + HALF_MINOR_TICK_SIZE);
} else {
g.drawLine(position - HALF_MINOR_TICK_SIZE, pos, position + HALF_MINOR_TICK_SIZE, pos);
}
}
}
/**
* Finds and sets the default maximum of the axis considering the maximum value of the data
* @param dataMax maximum value of the data
*/
private void findDefaultMax(double dataMax) {
// if there is no positive values the minimum is 0
if (dataMax <= 0) {
max = 0;
} else {
int maxTmp = 1;
while ((dataMax / maxTmp) >= 10) {
maxTmp *= 10;
}
max = maxTmp;
while (max < dataMax) {
max += maxTmp;
}
}
}
/**
* Finds and sets the default minimum of the axis considering the minimum value of the data
* @param dataMin minimum value of the data
*/
private void findDefaultMin(double dataMin) {
if (dataMin >= 0) {
// if there is no negative values the maximum is 0
min = 0;
} else {
int minTmp = -1;
while ((dataMin / minTmp) >= 10) {
minTmp *= 10;
}
min = minTmp;
while (min > dataMin) {
min += minTmp;
}
}
}
/**
* Finds and sets the default major and minor units
*/
private void findDefaultUnits() {
majorUnit = 1;
double range = max - min;
while ((range / majorUnit) >= 100) {
majorUnit *= 10;
}
minorUnit = majorUnit / 2;
}
/**
* @return the logBase
*/
public final LogBase getLogBase() {
return logBase;
}
/**
* @return the majorUnit of the ticks
*/
public final double getMajorUnit() {
return majorUnit;
}
/**
* @return the maximum of the axis
*/
public final double getMax() {
return max;
}
/**
* @return the minimum of the axis
*/
public final double getMin() {
return min;
}
/**
* @return the minorUnit of the ticks
*/
public final double getMinorUnit() {
return minorUnit;
}
/**
* @return the name of the axis
*/
public final String getName() {
return name;
}
/**
* @return the orientation of the axis
*/
public final boolean getOrientation() {
return orientation;
}
/**
* @return the position of the axis (y position for an horizontal axis and x position for a vertical one)
*/
public final int getPosition() {
return position;
}
/**
* @return the true if the axis is in a log scale
*/
public final boolean isLogScale() {
return isLogScale;
}
/**
* @return the true if the grid needs to be shown
*/
public final boolean isShowGrid() {
return showGrid;
}
/**
* @param logBase the logBase to set
*/
public final void setLogBase(LogBase logBase) {
this.logBase = logBase;
}
/**
* @param isLogScale the isLogScale to set
*/
public final void setLogScale(boolean isLogScale) {
this.isLogScale = isLogScale;
}
/**
* @param majorUnit the majorUnit of the ticks
*/
public final void setMajorUnit(double majorUnit) {
this.majorUnit = majorUnit;
}
/**
* @param max the maximum of the axis
*/
public final void setMax(double max) {
this.max = max;
}
/**
* @param min the minimum of the axis
*/
public final void setMin(double min) {
this.min = min;
}
/**
* @param minorUnit the minorUnit of the ticks
*/
public final void setMinorUnit(double minorUnit) {
this.minorUnit = minorUnit;
}
/**
* @param name the name of the axis
*/
public final void setName(String name) {
this.name = name;
}
/**
* @param position the position of the axis to set (y position for an horizontal axis and x position for a vertical one)
*/
public final void setPosition(int position) {
this.position = position;
}
/**
* @param showGrid show the grid if the parameter's value is true
*/
public final void setShowGrid(boolean showGrid) {
this.showGrid = showGrid;
}
}