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 }