/*
* Copyright (c) 2010-2012 Grid Dynamics Consulting Services, Inc, All Rights Reserved
* http://www.griddynamics.com
*
* This library is free software; you can redistribute it and/or modify it under the terms of
* the Apache License; either
* version 2.0 of the License, or any later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.griddynamics.jagger.diagnostics.visualization;
import edu.uci.ics.jung.algorithms.layout.*;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.VisualizationImageServer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.visualization.renderers.VertexLabelAsShapeRenderer;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ChainedTransformer;
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.Map;
public class GraphVisualizationHelper {
public enum GraphLayout {CIRCLE, ISOM, FR, KK}
public enum ColorTheme {LIGHT, DARK}
private static final double IMAGE_HORIZONTAL_MARGIN = 0.2;
private static final double IMAGE_VERTICAL_MARGIN = 0.2;
private static final int MAX_LABEL_LENGTH = 30;
public static <V, E> Image renderGraph(Graph<V, E> graph, int width, int height, GraphLayout graphLayout, final ColorTheme colorTheme, final Map<V, Paint> customNodeColors) {
Layout<V, E> layout;
switch (graphLayout) {
case CIRCLE : layout = new CircleLayout<V, E>(graph); break;
case ISOM : layout = new ISOMLayout<V, E>(graph); break;
case FR : layout = new FRLayout<V, E>(graph); break;
case KK : layout = new KKLayout<V, E>(graph); break;
default : throw new RuntimeException("Unknown Graph Layout : [" + graphLayout + "]");
}
layout.setSize(new Dimension((int)(width * (1 - IMAGE_HORIZONTAL_MARGIN)), (int)(height * (1 - IMAGE_VERTICAL_MARGIN))));
VisualizationImageServer<V, E> server = new VisualizationImageServer<V, E>(
displacementLayout(layout, (int)(width * IMAGE_HORIZONTAL_MARGIN/2), (int)(height * IMAGE_VERTICAL_MARGIN/2)),
new Dimension(width, height));
final Color edgeColor;
switch (colorTheme) {
case LIGHT : server.setBackground(Color.WHITE); edgeColor = Color.BLACK; break;
case DARK : server.setBackground(Color.BLACK); edgeColor = Color.LIGHT_GRAY; break;
default : throw new RuntimeException("Unknown Color Theme : [" + colorTheme + "]");
}
Transformer<V, Paint> vertexPaint = new Transformer<V, Paint>() {
public Paint transform(V v) {
Paint paint = customNodeColors.get(v);
if(paint == null) {
paint = Color.LIGHT_GRAY;
}
return paint;
}
};
Transformer<V, Paint> vertexBorderPaint = new Transformer<V, Paint>() {
public Paint transform(V v) {
return Color.DARK_GRAY;
}
};
Transformer<E, Paint> edgePaint = new Transformer<E, Paint>() {
public Paint transform(E e) {
return edgeColor;
}
};
server.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
server.getRenderContext().setEdgeDrawPaintTransformer(edgePaint);
server.getRenderContext().setArrowDrawPaintTransformer(edgePaint);
server.getRenderContext().setArrowFillPaintTransformer(edgePaint);
server.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<V>());
server.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<E>());
server.getRenderContext().setVertexDrawPaintTransformer(vertexBorderPaint);
server.getRenderContext().setVertexLabelTransformer(
new ChainedTransformer<V, String>(
new Transformer[]{
new ToStringLabeller<V>(),
new Transformer<String, String>() {
public String transform(String input) {
return "<html><center><p>" + formatLabel(input, MAX_LABEL_LENGTH);
}
}}));
VertexLabelAsShapeRenderer<V, E> vlasr = new VertexLabelAsShapeRenderer<V, E>(server.getRenderContext());
server.getRenderContext().setVertexShapeTransformer(vlasr);
server.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);
return server.getImage(new Point(0, 0), new Dimension(width, height));
}
private static String formatLabel(String string, int maxLength) {
StringBuffer buffer = new StringBuffer(string);
int runLength = 0;
for(int i = 0; i < buffer.length(); i++) {
if(buffer.charAt(i) != '\n') {
runLength++;
} else {
runLength = 0;
}
if(runLength >= maxLength) {
buffer.insert(i, '\n');
runLength = 0;
}
}
return buffer.toString().replaceAll("\\n", "<br/>");
}
private static <V, E> Layout<V, E> displacementLayout(Layout<V, E> layout, final int xOffset, final int yOffset) {
return new LayoutDecorator<V, E>(layout) {
@Override
public Point2D transform(V v) {
Point2D point = delegate.transform(v);
return new Point.Double(point.getX() + xOffset, point.getY() + yOffset);
}
};
}
}