/*
* #%L
* gitools-core
* %%
* Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group
* %%
* 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/gpl-3.0.html>.
* #L%
*/
package org.gitools.ui.app.heatmap.drawer;
import org.apache.commons.lang.StringUtils;
import org.gitools.api.matrix.MatrixDimensionKey;
import org.gitools.heatmap.Heatmap;
import org.gitools.heatmap.HeatmapDimension;
import org.gitools.heatmap.decorator.Decoration;
import org.gitools.ui.core.HeatmapPosition;
import org.gitools.utils.color.Colors;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
public abstract class AbstractHeatmapDrawer {
public static final Color SELECTED_COLOR = new Color(0, 0, 128, 60);
protected Heatmap heatmap;
protected HeatmapDimension rows;
protected HeatmapDimension columns;
private static OnScreenRect onScreenRect;
public static final int HIGHLIGHT_POLICY_NORMAL = 0;
public static final int HIGHLIGHT_POLICY_FORCE = 1;
public static final int HIGHLIGHT_POLICY_AVOID = 2;
private static final double radianAngle90 = (90.0 / 180.0) * Math.PI;
private boolean pictureMode;
protected AbstractHeatmapDrawer(Heatmap heatmap) {
this.heatmap = heatmap;
this.columns = heatmap.getColumns();
this.rows = heatmap.getRows();
this.pictureMode = false;
if (onScreenRect == null) {
this.onScreenRect = new OnScreenRect();
}
}
public Heatmap getHeatmap() {
return heatmap;
}
public void setHeatmap(Heatmap heatmap) {
this.heatmap = heatmap;
}
public boolean isPictureMode() {
return pictureMode;
}
public void setPictureMode(boolean pictureMode) {
this.pictureMode = pictureMode;
}
public abstract Dimension getSize();
/**
* Draw contents on the rectangle delimited by box using the clip.
*
* @param g Drawing device
* @param box The bounds of the total canvas
* @param clip The clip (inside the box)
*/
public abstract void draw(Graphics2D g, Rectangle box, Rectangle clip);
public abstract HeatmapPosition getPosition(Point p);
public abstract Point getPoint(HeatmapPosition p);
/**
* Paint background.
*
* @param backgroundColor the background color
* @param g the g
* @param box the box
*/
protected static void paintBackground(Color backgroundColor, Graphics2D g, Rectangle box) {
g.setColor(backgroundColor);
g.fillRect(box.x, box.y, box.width, box.height);
}
/**
* Automatically change the font size to fit in the cell height.
*
* @param g the Graphics2D object
* @param cellHeight the cell height
* @param minFontSize the min font size
* @return Returns true if the new font size fits in the cell height, false if it doesn't fit.
*/
protected static boolean calculateFontSize(Graphics2D g, int cellHeight, int minFontSize) {
float fontHeight = g.getFontMetrics().getHeight();
float fontSize = g.getFont().getSize();
while (fontHeight > (cellHeight - 2) && fontSize > minFontSize) {
fontSize--;
g.setFont(g.getFont().deriveFont(fontSize));
fontHeight = g.getFontMetrics().getHeight();
}
return fontHeight <= (cellHeight - 2);
}
protected static void paintCell(Decoration decoration, Color gridColor, int gridSize, int offsetX, int offsetY, int width, int height, Graphics2D g, Rectangle box) {
int y = box.y + offsetY;
int x = box.x + offsetX;
g.setColor(decoration.getBgColor());
g.fillRect(x, y, width, height);
g.setColor(gridColor);
g.fillRect(x, y + height, width, gridSize);
String text = decoration.getFormatedValue();
if (!StringUtils.isEmpty(text)) {
Font font = g.getFont();
boolean isRotated = decoration.isRotate();
int fontHeight = (int) font.getSize2D();
if (fontHeight <= (isRotated ? width : height)) {
int textWidth = (int) g.getFontMetrics().getStringBounds(text, g).getWidth();
//TODO: textWidth depends on SuperScript
if (textWidth < (isRotated ? height : width)) {
int leftMargin = ((width - textWidth) / 2) + 1;
int bottomMargin = ((height - fontHeight) / 2) + 1;
if (isRotated) {
leftMargin = ((width - fontHeight) / 2) + 1;
bottomMargin = height - (((height - textWidth) / 2));
}
g.setColor(Colors.bestForegroundColor(decoration.getBgColor()));
if (text.matches("[0-9\\.]+e-?[0-9]+")) {
int e_pos = text.indexOf("e") + 3;
text = text.replaceAll("e(-?[0-9]+)", "ยท10$1");
int superscriptEnd = text.length();
AttributedString attText = new AttributedString(text);
attText.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, e_pos, superscriptEnd);
if (isRotated) {
g.rotate(radianAngle90);
g.drawString(attText.getIterator(), y + height - bottomMargin, - x - leftMargin - 1);
g.rotate(-radianAngle90);
} else {
g.drawString(attText.getIterator(), x + leftMargin, y + height - bottomMargin);
}
} else {
if (isRotated) {
g.rotate(radianAngle90);
g.drawString(text, y + height - bottomMargin, - x - leftMargin - 1);
if ("CoCA-08".equals(text)) {
System.out.println("x = " + x + " leftMargin = " + leftMargin + " width = " + width + " fontHeight = " + fontHeight);
}
g.rotate(-radianAngle90);
} else {
g.drawString(text, x + leftMargin, y + height - bottomMargin);
}
}
}
}
}
}
protected void drawSelectedHighlightedAndFocus(Graphics2D g, Rectangle box, HeatmapDimension dimension, boolean drawingRows) {
drawSelectedHighlightedAndFocus(g, box, dimension, drawingRows, HIGHLIGHT_POLICY_NORMAL);
}
protected void drawSelectedHighlightedAndFocus(Graphics2D g, Rectangle box, HeatmapDimension dimension, boolean drawingRows, int highlightPolicy) {
// Draw selected
Color selectedColor = SELECTED_COLOR;
Color highlightedColor = dimension.getHighlightingColor();
int start = -1;
int end = -1;
if (dimension.getId().equals(MatrixDimensionKey.ROWS)) {
start = this.onScreenRect.rowStart;
end = this.onScreenRect.rowEnd;
} else {
start = this.onScreenRect.colStart;
end = this.onScreenRect.colEnd;
}
int cellSize = dimension.getFullCellSize();
for (int i = start; i <= end; i++) {
String id = dimension.getLabel(i);
Color paint = null;
// is selected
if (dimension.getSelected().contains(id)) {
paint = SELECTED_COLOR;
}
// is highlighted
if (highlightPolicy == HIGHLIGHT_POLICY_FORCE ||
(dimension.isHighlighted(id) && highlightPolicy != HIGHLIGHT_POLICY_AVOID)) {
paint = (paint == null) ? highlightedColor : blend(SELECTED_COLOR, highlightedColor);
}
// draw selection and highlight
if (paint != null) {
g.setColor(paint);
fillLine(g, box, i * cellSize, cellSize, drawingRows);
}
}
// Draw row lead
g.setColor(Color.DARK_GRAY);
int lead = dimension.indexOf(dimension.getFocus());
if (lead != -1) {
fillLine(g, box, (lead * cellSize) - 1, 1, drawingRows);
fillLine(g, box, ((lead + 1) * cellSize) - 1, 1, drawingRows);
}
}
/**
* Paint a full horizontal or vertical line
*/
protected void fillLine(Graphics2D g, Rectangle box, int position, int size, boolean drawingRows) {
if (drawingRows) {
g.fillRect(box.x, box.y + position, box.width, size);
} else {
g.fillRect(box.x + position, box.y, size, box.height);
}
}
public OnScreenRect getOnScreenRect() {
return this.onScreenRect;
}
public void setOnScreenRect(OnScreenRect onScreenRect) {
this.onScreenRect = onScreenRect;
}
public static Color blend(Color c0, Color c1) {
double totalAlpha = c0.getAlpha() + c1.getAlpha();
double weight0 = c0.getAlpha() / totalAlpha;
double weight1 = c1.getAlpha() / totalAlpha;
double r = weight0 * c0.getRed() + weight1 * c1.getRed();
double g = weight0 * c0.getGreen() + weight1 * c1.getGreen();
double b = weight0 * c0.getBlue() + weight1 * c1.getBlue();
double a = Math.max(c0.getAlpha(), c1.getAlpha());
return new Color((int) r, (int) g, (int) b, (int) a);
}
}