001 /*--------------------------------------------------------------------------+ 002 $Id: CushionTreeMapRenderer.java 26931 2010-03-17 14:53:13Z besenreu $ 003 | | 004 | Copyright 2005-2010 Technische Universitaet Muenchen | 005 | | 006 | Licensed under the Apache License, Version 2.0 (the "License"); | 007 | you may not use this file except in compliance with the License. | 008 | You may obtain a copy of the License at | 009 | | 010 | http://www.apache.org/licenses/LICENSE-2.0 | 011 | | 012 | Unless required by applicable law or agreed to in writing, software | 013 | distributed under the License is distributed on an "AS IS" BASIS, | 014 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 015 | See the License for the specific language governing permissions and | 016 | limitations under the License. | 017 +--------------------------------------------------------------------------*/ 018 package edu.tum.cs.commons.treemap; 019 020 import java.awt.Color; 021 import java.awt.Graphics2D; 022 import java.awt.geom.Rectangle2D; 023 024 025 /** 026 * A tree map renderer using "cushions" as described in J. van Wijk, H. van de 027 * Wetering: "Cushion Treemaps: Visualization of Hierarchical Information". 028 * 029 * @author Benjamin Hummel 030 * @author $Author: besenreu $ 031 * @version $Rev: 26931 $ 032 * @levd.rating GREEN Hash: 8731C2C4BC1A38B13F743E451FBF6A43 033 */ 034 public class CushionTreeMapRenderer implements ITreeMapRenderer { 035 036 /** The height parameter for the cushions. */ 037 private final double h; 038 039 /** The height scale factor. */ 040 private final double f; 041 042 /** 043 * Constructor. 044 * 045 * @param h 046 * the height parameter giving the heigt of the cushions relative 047 * to their size. 0.5 seems to be a reasonable value. 048 * @param f 049 * the scale factor used to reduce the heights of nested 050 * cushions. The value should be between 0 and 1, where smaller 051 * values will reduce the cushion effect. 052 */ 053 public CushionTreeMapRenderer(double h, double f) { 054 this.h = h; 055 this.f = f; 056 } 057 058 /** {@inheritDoc} */ 059 public <T> void renderTreeMap(ITreeMapNode<T> node, Graphics2D graphics) { 060 // use loop here, to avoid adding cushion to top level node 061 for (ITreeMapNode<T> child : node.getChildren()) { 062 render(child, graphics, h, new double[4]); 063 } 064 } 065 066 /** 067 * Renders the given node. 068 * 069 * @param node 070 * the node to render. 071 * @param g 072 * the graphics to render into. 073 * @param height 074 * the current height (already scaled for this level). 075 * @param coefs 076 * the coefficients of the local parabola. The incides 0 and 1 077 * give the coefficients for x^2 and x, while 2 and 3 are for y^2 078 * and y. The constant part is not needed. 079 */ 080 private <T> void render(ITreeMapNode<T> node, Graphics2D g, double height, 081 double[] coefs) { 082 Rectangle2D rect = node.getLayoutRectangle(); 083 double[] myCoefs = addLocalParabola(height, coefs, rect); 084 if (node.getChildren().isEmpty()) { 085 renderCushion(rect, myCoefs, g, node.getColor(), node 086 .getPatternColor(), node.getDrawingPattern()); 087 } else if (node.getChildren().size() == 1) { 088 // do not scale height or add cushion 089 render(node.getChildren().get(0), g, height, coefs); 090 } else { 091 for (ITreeMapNode<T> child : node.getChildren()) { 092 render(child, g, height * f, myCoefs); 093 } 094 } 095 } 096 097 /** Adds the local parabola to the given coefs and returns the result. */ 098 private double[] addLocalParabola(double height, double[] coefs, 099 Rectangle2D rect) { 100 double[] myCoefs = new double[4]; 101 double x1 = rect.getMinX(); 102 double x2 = rect.getMaxX(); 103 double y1 = rect.getMinY(); 104 double y2 = rect.getMaxY(); 105 myCoefs[0] = coefs[0] - 4 * height / (x2 - x1); 106 myCoefs[1] = coefs[1] + 4 * height * (x1 + x2) / (x2 - x1); 107 myCoefs[2] = coefs[2] - 4 * height / (y2 - y1); 108 myCoefs[3] = coefs[3] + 4 * height * (y1 + y2) / (y2 - y1); 109 return myCoefs; 110 } 111 112 /** Renders the given cushion. */ 113 private void renderCushion(Rectangle2D rect, double[] coefs, Graphics2D g, 114 Color baseColor, Color patternColor, IDrawingPattern drawingPattern) { 115 116 // light normal taken from the cited paper. 117 final double lx = 0.09759; 118 final double ly = 0.19518; 119 final double lz = 0.9759; 120 121 int minX = (int) (rect.getMinX() + .5); 122 int minY = (int) (rect.getMinY() + .5); 123 int maxX = (int) (rect.getMaxX() + .5); 124 int maxY = (int) (rect.getMaxY() + .5); 125 126 for (int x = minX; x < maxX; ++x) { 127 for (int y = minY; y < maxY; ++y) { 128 double nx = -(2 * coefs[0] * (x + .5) + coefs[1]); 129 double ny = -(2 * coefs[2] * (y + .5) + coefs[3]); 130 double norm = Math.sqrt(nx * nx + ny * ny + 1); 131 double cosa = (nx * lx + ny * ly + lz) / norm; 132 133 Color color = baseColor; 134 if (drawingPattern != null && drawingPattern.isForeground(x, y)) { 135 color = patternColor; 136 } 137 138 g.setColor(shadeColor(color, .2 + .8 * Math.max(0, cosa))); 139 g.drawLine(x, y, x, y); 140 } 141 } 142 } 143 144 /** 145 * Calculate the shaded color. 146 * 147 * @param color 148 * the base color. 149 * @param luminance 150 * a parameter between 0 and 1, where 0 corresponds to black and 151 * 1 to white. 152 */ 153 private Color shadeColor(Color color, double luminance) { 154 int base = 0; 155 luminance *= 2; 156 if (luminance > 1) { 157 luminance = 2 - luminance; 158 base = (int) (255 * (1 - luminance)); 159 } 160 161 return new Color((int) (color.getRed() * luminance) + base, 162 (int) (color.getGreen() * luminance) + base, (int) (color 163 .getBlue() * luminance) 164 + base); 165 } 166 }