/*
* RapidMiner
*
* Copyright (C) 2001-2008 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.viewer;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Iterator;
import javax.swing.JPanel;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.operator.learner.clustering.ClusterNode;
import com.rapidminer.operator.learner.clustering.HierarchicalClusterModel;
import com.rapidminer.report.Renderable;
/**
* Plots a dendrogram of a given cluster model. The nodes in the model must have different, non-NaN values for
* this operator to work.
*
* @author Michael Wurst
* @version $Id: DendrogramPlotter.java,v 1.6 2008/07/19 16:31:17 ingomierswa Exp $
*
*/
public class DendrogramPlotter extends JPanel implements Renderable {
private static final long serialVersionUID = 2892192060246909733L;
private static final int MARGIN = 10;
private HierarchicalClusterModel hcm;
private int numObjects;
private double maxWeight;
private double minWeight;
private int maxX;
private int maxY;
private int count;
private Color color = SwingTools.DARKEST_BLUE;
public DendrogramPlotter(HierarchicalClusterModel hcm) {
this.hcm = hcm;
numObjects = hcm.getRootNode().getNumberOfObjectsInSubtree();
minWeight = Double.POSITIVE_INFINITY;
maxWeight = Double.NEGATIVE_INFINITY;
findMinMaxWeight(hcm.getRootNode());
}
private void findMinMaxWeight(ClusterNode cn) {
if(maxWeight < cn.getWeight())
maxWeight = cn.getWeight();
if(minWeight > cn.getWeight())
minWeight = cn.getWeight();
for(Iterator<ClusterNode> it = cn.getSubNodes(); it.hasNext();)
findMinMaxWeight(it.next());
}
private void drawLine(int x1, int y1, int x2, int y2, Graphics g) {
g.setColor(color);
g.drawLine(x1, y1, x2, y2);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if((minWeight == maxWeight)||(Double.isNaN(minWeight))||(Double.isInfinite(minWeight))||
(Double.isNaN(maxWeight))||(Double.isInfinite(maxWeight))) {
g.drawString("Dendrogram not available for this cluster model. Use an agglomerative clusterer.", MARGIN, MARGIN + 15);
return;
}
this.maxX = getWidth() - 2 * MARGIN;
this.maxY = getHeight() - 2 * MARGIN;
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
Graphics translated = g.create();
translated.translate(MARGIN, MARGIN);
count = 0;
maxWeight = maxWeight + maxWeight * maxWeight;
paintRecursively(hcm.getRootNode(), hcm.getRootNode().getWeight(), translated);
}
private int weightToYPos(double weight) {
return (int) Math.round(maxY * (((weight) - minWeight) / ((maxWeight - minWeight))));
}
private int countToXPos(int count) {
return (int) Math.round((((double) count) /((double) numObjects)) * ((double) maxX));
}
private int paintRecursively(ClusterNode cn, double baseWeight, Graphics g) {
int leftPos = -1;
int rightPos = -1;
for(Iterator<ClusterNode> it= cn.getSubNodes(); it.hasNext();) {
ClusterNode subNode = it.next();
if((subNode.getNumberOfSubNodes() > 0)||(subNode.getNumberOfObjects() > 1)) {
int currentPos = paintRecursively(subNode, cn.getWeight(), g);
if(leftPos == -1)
leftPos = currentPos;
rightPos = currentPos;
}
}
for(Iterator<ClusterNode> it= cn.getSubNodes(); it.hasNext();) {
ClusterNode subNode = it.next();
if((subNode.getNumberOfObjects() == 1)&&(subNode.getNumberOfSubNodes() == 0)) {
drawLine(countToXPos(count), weightToYPos(cn.getWeight()), countToXPos(count), weightToYPos(maxWeight),g);
int currentPos = countToXPos(count);
if(leftPos == -1)
leftPos = currentPos;
rightPos = currentPos;
count++;
}
}
for(Iterator<String> it = cn.getObjects(); it.hasNext();) {
drawLine(countToXPos(count), weightToYPos(cn.getWeight()), countToXPos(count), weightToYPos(maxWeight),g);
int currentPos = countToXPos(count);
if(leftPos == -1)
leftPos = currentPos;
rightPos = currentPos;
count++;
it.next();
}
int middlePos = (rightPos + leftPos)/2;
drawLine(middlePos, weightToYPos(baseWeight) ,middlePos, weightToYPos(cn.getWeight()), g);
drawLine(leftPos, weightToYPos(cn.getWeight()) ,rightPos, weightToYPos(cn.getWeight()), g);
return middlePos;
}
public void prepareRendering() {}
public int getRenderHeight(int preferredHeight) {
int height = getHeight();
if (height < 1) {
height = preferredHeight;
}
return height;
}
public int getRenderWidth(int preferredWidth) {
int width = getWidth();
if (width < 1) {
width = preferredWidth;
}
return width;
}
public void render(Graphics graphics, int width, int height) {
setSize(width, height);
paint(graphics);
}
}