/* * #%L * gitools-utils * %% * 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.utils.colorscale; import org.gitools.utils.formatter.ITextFormatter; import java.awt.*; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; public class ColorScaleDrawer { private NumericColorScale scale; private final Color bgColor; private final int widthPadding; private final int heightPadding; private int barSize; private final Color barBorderColor; private final boolean legendEnabled; private final Color legendPointColor; private final int legendPadding; private final Font legendFont; private ITextFormatter textFormatter; public ColorScaleDrawer(INumericColorScale scale, ITextFormatter textFormatter) { setScale(scale); this.bgColor = Color.WHITE; this.widthPadding = 8; this.heightPadding = 8; this.barSize = 18; this.barBorderColor = Color.BLACK; this.legendEnabled = true; this.legendPointColor = Color.BLACK; this.legendPadding = 4; this.legendFont = new Font(Font.SANS_SERIF, Font.PLAIN, 10); this.textFormatter = textFormatter; } public INumericColorScale getScale() { return scale; } void setScale(INumericColorScale scale) { this.scale = (NumericColorScale) scale; } public void draw(Graphics2D g, Rectangle bounds, Rectangle clip, String name) { g.setColor(bgColor); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); bounds.x += widthPadding; bounds.width -= widthPadding * 2; bounds.y += heightPadding; bounds.height -= heightPadding * 2; int scaleXLeft = bounds.x; int scaleXRight = scaleXLeft + bounds.width - 1; int scaleYTop = bounds.y; int scaleYBottom = scaleYTop + barSize - 1; int scaleWidth = scaleXRight - scaleXLeft; int scaleHeight = scaleYBottom - scaleYTop; ArrayList<ColorScaleRange> ranges = scale.getScaleRanges(); double minRange = adjustRangeWidths(scaleWidth, ranges); calculateFontSize(g, Double.valueOf(minRange).intValue(), 8); int rangeXLeft = scaleXLeft; for (ColorScaleRange range : ranges) { int rangeWidth = (int) range.getWidth(); int rangeEnd = rangeXLeft + rangeWidth; //paint the colored box of scale range if (!(range.getType().equals(ColorScaleRange.EMPTY_TYPE))) { for (int x = rangeXLeft; x <= rangeEnd; x++) { double value = getValueForX(x, range.getType(), rangeXLeft, rangeEnd, range.getMinValue(), range.getMaxValue()); final Color color = scale.valueColor(value); g.setColor(color); g.drawLine(x, scaleYTop, x, scaleYBottom); } } //paint Border if (range.isBorderEnabled()) { g.setColor(barBorderColor); g.drawRect(rangeXLeft, scaleYTop, rangeWidth, scaleHeight); } //paint legend if (legendEnabled) { int lastX = rangeXLeft; int fontHeight = g.getFontMetrics().getHeight(); int legendYTop = scaleYBottom + legendPadding; int ye = legendYTop + fontHeight; g.setColor(legendPointColor); if (range.getLeftLabel() != null) { String legend = textFormatter.format(range.getLeftLabel()); int fontWidth = g.getFontMetrics().stringWidth(legend); int legendStart = rangeXLeft + legendPadding; g.drawString(legend, legendStart, ye); lastX = fontWidth + legendPadding; } if (range.getCenterLabel() != null) { String legend = textFormatter.format(range.getCenterLabel()); int fontWidth = g.getFontMetrics().stringWidth(legend); int legendStart = rangeEnd - (rangeWidth / 2 + fontWidth / 2); if (legendStart > lastX) { g.drawString(legend, legendStart, ye); lastX = legendStart + fontWidth + legendPadding; } } if (range.getRightLabel() != null) { String legend = textFormatter.format(range.getRightLabel()); int fontWidth = g.getFontMetrics().stringWidth(legend); int legendStart = rangeXLeft + rangeWidth - legendPadding - fontWidth; if (legendStart > lastX) { g.drawString(legend, legendStart, ye); } } } rangeXLeft = rangeEnd; } g.setFont(g.getFont().deriveFont(Font.BOLD)); g.drawString(name, scaleXLeft, scaleYBottom + legendPadding * 2 + g.getFontMetrics().getHeight()*2); } private double getValueForX(int x, String type, int minX, int maxX, double minValue, double maxValue) { if (type.equals(ColorScaleRange.LINEAR_TYPE)) { double delta = (maxValue - minValue) / (maxX - minX); return minValue + delta * (x - minX); } else if (type.equals(ColorScaleRange.LOGARITHMIC_TYPE)) { double exp = 1.0 * (x - minX) / (maxX - minX); return minValue + ((Math.pow(10, exp) - 1) / 9) * (maxValue - minValue); } else if (type.equals(ColorScaleRange.CONSTANT_TYPE)) { return maxValue; } return 0; } private double adjustRangeWidths(int scaleWidth, ArrayList<ColorScaleRange> ranges) { double rangesWidth = 0; double minRange = Double.MAX_VALUE; for (ColorScaleRange r : ranges) { rangesWidth += r.getWidth(); } HashSet rangesSet = new HashSet<>(ranges); double resizeFactor = scaleWidth / rangesWidth; Iterator iter = rangesSet.iterator(); while (iter.hasNext()) { ColorScaleRange r = (ColorScaleRange) iter.next(); double newWidth = r.getWidth() * resizeFactor; r.setWidth(newWidth); if (newWidth < minRange) { minRange = newWidth; } } return minRange; } public Dimension getSize() { int height = heightPadding * 2 + barSize; if (legendEnabled) { BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); Graphics g = image.getGraphics(); height += (g.getFontMetrics(legendFont).getHeight() + legendPadding) * 2 ; } int width = widthPadding + 20; return new Dimension(width, height); } /** * Automatically change the font size to fit in the range width * * @param g the Graphics2D object * @param rangeWidth 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 rangeWidth, int minFontSize) { float fontHeight = g.getFontMetrics().getHeight(); float fontSize = g.getFont().getSize(); while (fontHeight > (rangeWidth - 2) && fontSize > minFontSize) { fontSize--; g.setFont(g.getFont().deriveFont(fontSize)); fontHeight = g.getFontMetrics().getHeight(); } return fontHeight <= (rangeWidth - 2); } }