/*
* 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.graphs;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.util.Context;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.VertexLabelRenderer;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.visualization.transform.BidirectionalTransformer;
import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator;
import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer;
import edu.uci.ics.jung.visualization.transform.shape.TransformingGraphics;
/**
* This renderer is used for rendering the labels of the tree model nodes.
*
* @author Ingo Mierswa
* @version $Id: TreeModelNodeLabelRenderer.java,v 1.7 2008/05/09 19:23:24 ingomierswa Exp $
*
* @param <V> the type for vertices
* @param <E> the type for edges
*/
public class TreeModelNodeLabelRenderer<V,E> implements Renderer.VertexLabel<V,E> {
/** Used for positioning the label inside of a node, */
public static class InsidePositioner implements Positioner {
public Position getPosition(float x, float y, Dimension d) {
int cx = d.width/2;
int cy = d.height/2;
if(x > cx && y > cy) return Position.NW;
if(x > cx && y < cy) return Position.SW;
if(x < cx && y > cy) return Position.NE;
return Position.SE;
}
}
/** Used for positioning the label outside of a node, */
public static class OutsidePositioner implements Positioner {
public Position getPosition(float x, float y, Dimension d) {
int cx = d.width/2;
int cy = d.height/2;
if(x > cx && y > cy) return Position.SE;
if(x > cx && y < cy) return Position.NE;
if(x < cx && y > cy) return Position.SW;
return Position.NW;
}
}
private static final int LABEL_OFFSET_Y = -7;
protected Position position = Position.CNTR;
private Positioner positioner = new OutsidePositioner();
private TreeModelGraphCreator graphCreator;
public TreeModelNodeLabelRenderer(TreeModelGraphCreator graphCreator) {
this.graphCreator = graphCreator;
}
/**
* @return the position
*/
public Position getPosition() {
return position;
}
/**
* @param position the position to set
*/
public void setPosition(Position position) {
this.position = position;
}
public Component prepareRenderer(RenderContext<V,E> rc, VertexLabelRenderer graphLabelRenderer, Object value,
boolean isSelected, V vertex) {
return rc.getVertexLabelRenderer().<V>getVertexLabelRendererComponent(rc.getScreenDevice(), value,
rc.getVertexFontTransformer().transform(vertex), isSelected, vertex);
}
/**
* Labels the specified vertex with the specified label.
* Uses the font specified by this instance's
* <code>VertexFontFunction</code>. (If the font is unspecified, the existing
* font for the graphics context is used.) If vertex label centering
* is active, the label is centered on the position of the vertex; otherwise
* the label is offset slightly.
*/
public void labelVertex(RenderContext<V,E> rc, Layout<V,E> layout, V v, String label) {
Graph<V,E> graph = layout.getGraph();
if (rc.getVertexIncludePredicate().evaluate(Context.<Graph<V,E>,V>getInstance(graph,v)) == false) {
return;
}
Point2D pt = layout.transform(v);
pt = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, pt);
float x = (float) pt.getX();
float y = (float) pt.getY();
Component component = prepareRenderer(rc, rc.getVertexLabelRenderer(), label,
rc.getPickedVertexState().isPicked(v), v);
GraphicsDecorator g = rc.getGraphicsContext();
Dimension d = component.getPreferredSize();
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
Shape shape = rc.getVertexShapeTransformer().transform(v);
shape = xform.createTransformedShape(shape);
if(rc.getGraphicsContext() instanceof TransformingGraphics) {
BidirectionalTransformer transformer = ((TransformingGraphics)rc.getGraphicsContext()).getTransformer();
if(transformer instanceof ShapeTransformer) {
ShapeTransformer shapeTransformer = (ShapeTransformer)transformer;
shape = shapeTransformer.transform(shape);
}
}
Rectangle2D bounds = shape.getBounds2D();
Point p = null;
if(position == Position.AUTO) {
Dimension vvd = rc.getScreenDevice().getSize();
if(vvd.width == 0 || vvd.height == 0) {
vvd = rc.getScreenDevice().getPreferredSize();
}
p = getAnchorPoint(bounds, d, positioner.getPosition(x, y, vvd));
} else {
p = getAnchorPoint(bounds, d, position);
}
if (graphCreator.isLeaf((String)v)) {
p.setLocation(p.x, p.y + LABEL_OFFSET_Y);
}
g.draw(component, rc.getRendererPane(), p.x, p.y, d.width, d.height, true);
}
protected Point getAnchorPoint(Rectangle2D vertexBounds, Dimension labelSize, Position position) {
double x;
double y;
int offset = 5;
switch(position) {
case N:
x = vertexBounds.getCenterX()-labelSize.width/2.0d;
y = vertexBounds.getMinY()-offset - labelSize.height;
return new Point((int)x,(int)y);
case NE:
x = vertexBounds.getMaxX()+offset;
y = vertexBounds.getMinY()-offset-labelSize.height;
return new Point((int)x,(int)y);
case E:
x = vertexBounds.getMaxX()+offset;
y = vertexBounds.getCenterY()-labelSize.height/2.0d;
return new Point((int)x,(int)y);
case SE:
x = vertexBounds.getMaxX()+offset;
y = vertexBounds.getMaxY()+offset;
return new Point((int)x,(int)y);
case S:
x = vertexBounds.getCenterX()-labelSize.width/2.0d;
y = vertexBounds.getMaxY()+offset;
return new Point((int)x,(int)y);
case SW:
x = vertexBounds.getMinX()-offset-labelSize.width;
y = vertexBounds.getMaxY()+offset;
return new Point((int)x,(int)y);
case W:
x = vertexBounds.getMinX()-offset-labelSize.width;
y = vertexBounds.getCenterY()-labelSize.height/2.0d;
return new Point((int)x,(int)y);
case NW:
x = vertexBounds.getMinX()-offset-labelSize.width;
y = vertexBounds.getMinY()-offset-labelSize.height;
return new Point((int)x,(int)y);
case CNTR:
x = vertexBounds.getCenterX()-labelSize.width/2.0d;
y = vertexBounds.getCenterY()-labelSize.height/2.0d;
return new Point((int)x,(int)y);
default:
return new Point();
}
}
/**
* @return the positioner
*/
public Positioner getPositioner() {
return positioner;
}
/**
* @param positioner the positioner to set
*/
public void setPositioner(Positioner positioner) {
this.positioner = positioner;
}
}