/*
* #%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.gitools.heatmap.Heatmap;
import org.gitools.heatmap.HeatmapDimension;
import org.gitools.heatmap.HeatmapLayer;
import org.gitools.heatmap.decorator.Decoration;
import org.gitools.heatmap.decorator.Decorator;
import org.gitools.ui.core.Application;
import org.gitools.ui.core.HeatmapPosition;
import org.gitools.ui.platform.progress.JobThread;
import org.gitools.ui.platform.settings.Settings;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class HeatmapLayerBodyDrawer extends AbstractHeatmapDrawer {
private static final AffineTransformOp TRANSFORM_OP = new AffineTransformOp(AffineTransform.getScaleInstance(1, 1), AffineTransformOp.TYPE_BILINEAR);
private BufferedImage bufferedImage;
private boolean redrawBufferedImage = true;
private int svgLimit;
public HeatmapLayerBodyDrawer(Heatmap heatmap) {
super(heatmap);
svgLimit = Settings.get().getSvgBodyLimit();
PropertyChangeListener redrawImageListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
redrawBufferedImage = true;
}
};
heatmap.getRows().addPropertyChangeListener(HeatmapDimension.PROPERTY_VISIBLE, redrawImageListener);
heatmap.getColumns().addPropertyChangeListener(HeatmapDimension.PROPERTY_GRID_COLOR, redrawImageListener);
heatmap.getColumns().addPropertyChangeListener(HeatmapDimension.PROPERTY_VISIBLE, redrawImageListener);
heatmap.getColumns().addPropertyChangeListener(HeatmapDimension.PROPERTY_GRID_COLOR, redrawImageListener);
heatmap.getLayers().getTopLayer().getDecorator().addPropertyChangeListener(redrawImageListener);
heatmap.getLayers().addPropertyChangeListener(redrawImageListener);
}
@Override
public void draw(Graphics2D g, Rectangle box, Rectangle clip) {
int rowsGridSize = (rows.showGrid() ? rows.getGridSize() : 0);
int columnsGridSize = (columns.showGrid() ? columns.getGridSize() : 0);
int cellWidth = columns.getCellSize() + columnsGridSize;
int cellHeight = rows.getCellSize() + rowsGridSize;
int rowStart = (clip.y - box.y) / cellHeight;
int rowEnd = (clip.y - box.y + clip.height + cellHeight - 1) / cellHeight;
rowEnd = rowEnd < rows.size() ? rowEnd : rows.size();
int colStart = (clip.x - box.x) / cellWidth;
int colEnd = (clip.x - box.x + clip.width + cellWidth - 1) / cellWidth;
colEnd = colEnd < columns.size() ? colEnd : columns.size();
if (isPictureMode() && ((columns.size() * rows.size()) <= svgLimit)) {
redraw(g, box, rowsGridSize, columnsGridSize, cellWidth, cellHeight, rowStart, rowEnd, colStart, colEnd);
return;
}
OnScreenRect newRect = new OnScreenRect(rowStart, rowEnd, colStart, colEnd, box.width, box.height, heatmap);
//clip is sometimes corrupted : too small (when going through the menus)
boolean corruptedClip = newRect.within(getOnScreenRect());
redrawBufferedImage = !JobThread.isRunning() && (redrawBufferedImage || !newRect.equals(getOnScreenRect()));
if (redrawBufferedImage || isPictureMode()) {
Application.get().setCursorWaiting();
bufferedImage = new BufferedImage(clip.width, clip.height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D gb = bufferedImage.createGraphics();
gb.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// Clear background
gb.setColor(Color.WHITE);
gb.fillRect(0, 0, clip.width, clip.height);
redraw(gb, box, rowsGridSize, columnsGridSize, cellWidth, cellHeight, rowStart, rowEnd, colStart, colEnd);
Application.get().setCursorNormal();
setOnScreenRect(newRect);
redrawBufferedImage = false;
}
// offset for first
g.drawImage(bufferedImage, TRANSFORM_OP, colStart * cellWidth, rowStart * cellHeight);
}
private void redraw(Graphics2D g, Rectangle box, int rowsGridSize, int columnsGridSize, int cellWidth, int cellHeight, int rowStart, int rowEnd, int colStart, int colEnd) {
g.setFont(heatmap.getLayers().getTopLayer().getFont());
calculateFontSize(g, rows.getCellSize(), 7);
Decoration decoration = new Decoration();
int y = 0;
for (int row = rowStart; row < rowEnd; row++) {
int x = 0;
for (int col = colStart; col < colEnd; col++) {
if (heatmap.isDiagonal() && col < row) {
decoration.setBgColor(Color.WHITE);
decoration.setValue("");
} else {
decorateCell(decoration.reset(), row, col);
}
Color rowsGridColor = rows.getGridColor();
Color colsGridColor = columns.getGridColor();
g.setColor(colsGridColor);
g.fillRect(x - columnsGridSize, y, columnsGridSize, cellHeight);
paintCell(decoration, rowsGridColor, rowsGridSize, x, y, cellWidth - columnsGridSize, cellHeight - rowsGridSize, g, box);
x += cellWidth;
}
y += cellHeight;
}
}
protected void decorateCell(Decoration decoration, int row, int col) {
HeatmapLayer topLayer = heatmap.getLayers().getTopLayer();
Decorator decorator = topLayer.getDecorator();
String rowId = heatmap.getRows().getLabel(row);
String columnId = heatmap.getColumns().getLabel(col);
decorator.decorate(decoration, topLayer.getShortFormatter(), heatmap, topLayer, rowId, columnId);
}
@Override
public Dimension getSize() {
return new Dimension(columns.getFullCellSize() * columns.size(), rows.getFullCellSize() * rows.size());
}
@Override
public HeatmapPosition getPosition(Point p) {
int cellHeight = rows.getFullCellSize();
int rowCount = rows.size();
int totalHeight = cellHeight * rowCount;
int cellWidth = columns.getFullCellSize();
int columnCount = columns.size();
int totalWidth = cellWidth * columnCount;
int row = p.y >= 0 && p.y < totalHeight ? p.y / cellHeight : -1;
int column = p.x >= 0 && p.x < totalWidth ? p.x / cellWidth : -1;
return new HeatmapPosition(heatmap, row, column);
}
@Override
public Point getPoint(HeatmapPosition p) {
int cellHeight = rows.getFullCellSize();
int rowCount = rows.size();
int totalHeight = cellHeight * rowCount;
int cellWidth = columns.getFullCellSize();
int columnCount = columns.size();
int totalWidth = cellWidth * columnCount;
int x = p.column >= 0 ? p.column * cellWidth : 0;
if (x > totalWidth) {
x = totalWidth;
}
int y = p.row >= 0 ? p.row * cellHeight : 0;
if (y > totalHeight) {
y = totalHeight;
}
return new Point(x, y);
}
}