/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.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.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.text.StyleContext;
import org.apache.commons.collections15.Transformer;
import com.rapidminer.RapidMiner;
import com.rapidminer.gui.actions.export.PrintableComponent;
import com.rapidminer.gui.graphs.actions.ZoomInAction;
import com.rapidminer.gui.graphs.actions.ZoomOutAction;
import com.rapidminer.gui.look.Colors;
import com.rapidminer.gui.look.RapidLookTools;
import com.rapidminer.gui.properties.PropertyPanel;
import com.rapidminer.gui.tools.ExtendedJScrollPane;
import com.rapidminer.gui.tools.ExtendedJToolBar;
import com.rapidminer.gui.tools.IconSize;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.report.Renderable;
import com.rapidminer.tools.I18N;
import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.Context;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.MultiLayerTransformer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.ConstantDirectionalEdgeValueTransformer;
import edu.uci.ics.jung.visualization.decorators.DirectionalEdgeArrowTransformer;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.layout.LayoutTransition;
import edu.uci.ics.jung.visualization.renderers.BasicVertexRenderer;
import edu.uci.ics.jung.visualization.renderers.EdgeLabelRenderer;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.visualization.renderers.VertexLabelRenderer;
import edu.uci.ics.jung.visualization.util.Animator;
import edu.uci.ics.jung.visualization.util.ArrowFactory;
/**
* The basic graph viewer component for graph display.
*
* @author Ingo Mierswa, Marco Boeck
*/
public class GraphViewer<V, E> extends JPanel implements Renderable, PrintableComponent {
private static final long serialVersionUID = -7501422172633548861L;
private static final Color EDGE_LIGHT_GRAY = new Color(230, 230, 230);
private static final Color LEAF_BACKGROUND = new Color(230, 230, 230);
private static final Color BORDER_INVIS = new Color(0, 0, 0, 0);
public static final Color NODE_SELECTED = Colors.TEXT_HIGHLIGHT_BACKGROUND;
public static final Color NODE_BACKGROUND = new Color(230, 230, 230);
public static final Color NODE_ON_PATH = NODE_SELECTED;
public static final Font EDGE_FONT = StyleContext.getDefaultStyleContext().getFont("Open Sans", Font.PLAIN, 12);
public static final Font EDGE_FONT_ON_PATH = StyleContext.getDefaultStyleContext().getFont("Open Sans Semibold",
Font.BOLD, 13);
public static final Font VERTEX_BOLD_FONT = StyleContext.getDefaultStyleContext().getFont("Open Sans Semibold",
Font.BOLD, 14);
public static final Font VERTEX_PLAIN_FONT = StyleContext.getDefaultStyleContext().getFont("Open Sans Semibold",
Font.BOLD, 14);
private VisualizationViewer<V, E> vv;
private transient Layout<V, E> layout;
private transient GraphCreator<V, E> graphCreator;
private LayoutSelection<V, E> layoutSelection;
private transient ScalingControl scaler = new CrossoverScalingControl();
private transient SingleDefaultGraphMouse<V, E> graphMouse;
private boolean showEdgeLabels = true;
private boolean showVertexLabels = true;
private transient JSplitPane objectViewerSplitPane;
public GraphViewer(final GraphCreator<V, E> graphCreator) {
try {
if (!RapidMiner.getExecutionMode().isHeadless()) {
graphMouse = new SingleDefaultGraphMouse<>(1 / 1.1f, 1.1f);
}
} catch (HeadlessException e) {
}
this.graphCreator = graphCreator;
setLayout(new BorderLayout());
Graph<V, E> graph = graphCreator.createGraph();
this.layoutSelection = new LayoutSelection<>(this, graph);
this.layoutSelection.putClientProperty(RapidLookTools.PROPERTY_INPUT_BACKGROUND_DARK, true);
this.layoutSelection.setPreferredSize(new Dimension(200, PropertyPanel.VALUE_CELL_EDITOR_HEIGHT));
this.layout = new ISOMLayout<>(graph);
vv = new VisualizationViewer<V, E>(layout) {
private static final long serialVersionUID = 8247229781249216143L;
private boolean initialized = false;
/**
* Necessary in order to re-change layout after first painting (starting position and
* size).
*/
@Override
public void paint(Graphics g) {
super.paint(g);
if (!initialized) {
initialized = true;
updateLayout();
if (objectViewerSplitPane != null) {
objectViewerSplitPane.setDividerLocation(0.9);
}
}
}
};
vv.getRenderingHints().put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
vv.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
vv.getRenderingHints().put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
vv.setBorder(BorderFactory.createEmptyBorder());
vv.setBackground(Colors.WHITE);
SwingTools.disableClearType(vv);
// === design ===
setupRendering(graphCreator);
// === end of design ===
// === main panel ===
if (graphCreator.getObjectViewer() == null) {
vv.setBorder(BorderFactory.createMatteBorder(10, 0, 5, 10, Colors.WHITE));
add(vv, BorderLayout.CENTER);
} else {
JComponent viewer = graphCreator.getObjectViewer().getViewerComponent();
if (viewer != null) {
viewer.setBorder(null);
objectViewerSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
objectViewerSplitPane.setResizeWeight(1);
objectViewerSplitPane.add(vv, 0);
objectViewerSplitPane.add(viewer, 1);
objectViewerSplitPane.setBorder(BorderFactory.createMatteBorder(10, 0, 5, 10, Colors.WHITE));
add(objectViewerSplitPane, BorderLayout.CENTER);
} else {
vv.setBorder(BorderFactory.createMatteBorder(10, 0, 5, 10, Colors.WHITE));
add(vv, BorderLayout.CENTER);
}
}
// === control panel ===
Component controlPanel = createControlPanel();
JScrollPane sp = new ExtendedJScrollPane(controlPanel);
sp.setBorder(null);
add(sp, BorderLayout.WEST);
this.showEdgeLabels = !graphCreator.showEdgeLabelsDefault();
togglePaintEdgeLabels();
this.showVertexLabels = !graphCreator.showVertexLabelsDefault();
togglePaintVertexLabels();
this.layoutSelection.setLayout();
}
public LayoutSelection<V, E> getLayoutSelection() {
return this.layoutSelection;
}
private JComponent createControlPanel() {
// === mouse behaviour ===
if (graphMouse != null) {
vv.setGraphMouse(graphMouse);
}
vv.addGraphMouseListener(new GraphMouseListener<V>() {
@Override
public void graphClicked(V vertex, MouseEvent arg1) {
if (graphCreator.getObjectViewer() != null) {
vv.getPickedVertexState().clear();
vv.getPickedVertexState().pick(vertex, true);
graphCreator.getObjectViewer().showObject(graphCreator.getObject(vertex));
}
}
@Override
public void graphPressed(V arg0, MouseEvent arg1) {}
@Override
public void graphReleased(V vertex, MouseEvent arg1) {}
});
JPanel controls = new JPanel();
controls.setOpaque(true);
controls.setBackground(Colors.WHITE);
controls.setBorder(BorderFactory.createMatteBorder(10, 5, 5, 10, Colors.WHITE));
GridBagLayout gbLayout = new GridBagLayout();
controls.setLayout(gbLayout);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(4, 4, 4, 4);
c.weightx = 1;
c.weighty = 0;
// zooming
JToolBar zoomBar = new ExtendedJToolBar();
zoomBar.setLayout(new FlowLayout(FlowLayout.CENTER));
zoomBar.setBorder(BorderFactory.createTitledBorder("Zoom"));
zoomBar.setBackground(Colors.WHITE);
JButton zoomIn = new JButton(new ZoomInAction(this, IconSize.SMALL));
zoomIn.setContentAreaFilled(false);
zoomIn.setText("");
zoomBar.add(zoomIn);
JButton zoomOut = new JButton(new ZoomOutAction(this, IconSize.SMALL));
zoomOut.setContentAreaFilled(false);
zoomOut.setText("");
zoomBar.add(zoomOut);
c.gridwidth = GridBagConstraints.REMAINDER;
gbLayout.setConstraints(zoomBar, c);
controls.add(zoomBar);
// layout selection
c.gridwidth = GridBagConstraints.REMAINDER;
gbLayout.setConstraints(layoutSelection, c);
controls.add(layoutSelection);
// show node labels
JCheckBox nodeLabels = new JCheckBox("Node Labels", graphCreator.showVertexLabelsDefault());
nodeLabels.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
togglePaintVertexLabels();
}
});
c.gridwidth = GridBagConstraints.REMAINDER;
gbLayout.setConstraints(nodeLabels, c);
controls.add(nodeLabels);
// show edge labels
JCheckBox edgeLabels = new JCheckBox("Edge Labels", graphCreator.showEdgeLabelsDefault());
edgeLabels.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
togglePaintEdgeLabels();
}
});
c.gridwidth = GridBagConstraints.REMAINDER;
gbLayout.setConstraints(edgeLabels, c);
controls.add(edgeLabels);
// option components
for (int i = 0; i < graphCreator.getNumberOfOptionComponents(); i++) {
JComponent optionComponent = graphCreator.getOptionComponent(this, i);
if (optionComponent != null) {
c.gridwidth = GridBagConstraints.REMAINDER;
gbLayout.setConstraints(optionComponent, c);
controls.add(optionComponent);
}
}
JLabel filler = new JLabel();
c.weighty = 1.0;
c.gridwidth = GridBagConstraints.REMAINDER;
gbLayout.setConstraints(filler, c);
controls.add(filler);
return controls;
}
public void updateLayout() {
changeLayout(this.layout, this.layoutSelection.getAnimate(), 0, 0);
}
public void changeLayout(final Layout<V, E> newLayout, boolean animate, int desiredWidth, int desiredHeight) {
if (newLayout != null) {
MultiLayerTransformer transformer = vv.getRenderContext().getMultiLayerTransformer();
double scale = transformer.getTransformer(Layer.VIEW).getScale();
// set desired size
if (desiredWidth > 0 && desiredHeight > 0) {
vv.setSize(desiredWidth, desiredHeight);
try {
newLayout.setSize(new Dimension(desiredWidth, desiredHeight));
} catch (Exception e) {
// do nothing - some layouts do not support setting of size
}
} else {
int layoutWidth = (int) (vv.getWidth() / scale);
int layoutHeight = (int) (vv.getHeight() / scale);
try {
newLayout.setSize(new Dimension(layoutWidth, layoutHeight));
} catch (Exception e) {
// do nothing - some layouts do not support setting of size
}
}
if (layout == null) {
// initial layout --> no transition possible!
vv.setGraphLayout(newLayout);
} else {
// No transition possible if no edges in graph!
if (animate) {
if (newLayout.getGraph().getEdgeCount() > 0 || newLayout.getGraph().getVertexCount() > 0) {
try {
LayoutTransition<V, E> lt = new LayoutTransition<>(vv, layout, newLayout);
Animator animator = new Animator(lt);
animator.start();
} catch (Throwable e) {
// any error --> no transition possible!
vv.setGraphLayout(newLayout);
}
}
} else {
vv.setGraphLayout(newLayout);
}
}
this.layout = newLayout;
vv.scaleToLayout(this.scaler);
double viewX = transformer.getTransformer(Layer.VIEW).getTranslateX();
double viewY = transformer.getTransformer(Layer.VIEW).getTranslateX();
double scaleViewX = viewX * scale;
double scaleViewY = viewY * scale;
transformer.getTransformer(Layer.VIEW).translate(-scaleViewX, -scaleViewY);
Thread changeToStaticThread = new Thread() {
@Override
public void run() {
try {
sleep(10000);
} catch (InterruptedException e) {
// do nothing
}
// change to static
if (GraphViewer.this.layout.equals(newLayout)) { // still the same layout?
vv.setGraphLayout(new StaticLayout<>(layout.getGraph(), layout));
}
}
};
changeToStaticThread.start();
}
}
/**
* VertexLabel is not parameterized in Jung. In order to avoid to make all things unchecked, the
* default label position setting is done in this method.
*/
private void setDefaultLabelPosition() {
vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);
}
public void zoomIn() {
scaler.scale(vv, 1.1f, vv.getCenter());
}
public void zoomOut() {
scaler.scale(vv, 1 / 1.1f, vv.getCenter());
}
private void togglePaintEdgeLabels() {
setPaintEdgeLabels(!showEdgeLabels);
}
public void setPaintEdgeLabels(boolean showEdgeLabels) {
this.showEdgeLabels = showEdgeLabels;
if (this.showEdgeLabels) {
vv.getRenderContext().setEdgeLabelTransformer(new Transformer<E, String>() {
@Override
public String transform(E object) {
return graphCreator.getEdgeName(object);
}
});
} else {
vv.getRenderContext().setEdgeLabelTransformer(new Transformer<E, String>() {
@Override
public String transform(E object) {
return null;
}
});
}
vv.repaint();
}
private void togglePaintVertexLabels() {
setPaintVertexLabels(!showVertexLabels);
}
public void setPaintVertexLabels(boolean showVertexLabels) {
this.showVertexLabels = showVertexLabels;
if (this.showVertexLabels) {
Renderer.Vertex<V, E> vertexRenderer = graphCreator.getVertexRenderer();
if (vertexRenderer != null) {
vv.getRenderer().setVertexRenderer(vertexRenderer);
}
vv.getRenderContext().setVertexShapeTransformer(new ExtendedVertexShapeTransformer<>(graphCreator));
vv.getRenderContext().setVertexLabelTransformer(new Transformer<V, String>() {
@Override
public String transform(V object) {
return graphCreator.getVertexName(object);
}
});
} else {
vv.getRenderer().setVertexRenderer(new BasicVertexRenderer<V, E>());
vv.getRenderContext().setVertexShapeTransformer(new BasicVertexShapeTransformer<V>());
vv.getRenderContext().setVertexLabelTransformer(new Transformer<V, String>() {
@Override
public String transform(V object) {
return null;
}
});
}
vv.repaint();
}
public GraphCreator<V, E> getGraphCreator() {
return this.graphCreator;
}
public Component getVisualizationComponent() {
return vv;
}
@Override
public void prepareRendering() {
// this dirty hack was copied from the AbstractGraphRenderer
// this method is NOT used by Studio, only by the reporting extension
try {
// necessary to give layout (thread!) time to finish its work before reporting
Thread.sleep(1000);
} catch (InterruptedException e) {
// do nothing
}
}
@Override
public void finishRendering() {}
@Override
public int getRenderHeight(int preferredHeight) {
int height = vv.getHeight();
if (height < 1) {
height = preferredHeight;
}
return height;
}
@Override
public int getRenderWidth(int preferredWidth) {
int width = vv.getWidth();
if (width < 1) {
width = preferredWidth;
}
return width;
}
@Override
public void render(Graphics graphics, int width, int height) {
vv.setSize(width, height);
vv.setBorder(BorderFactory.createEmptyBorder());
changeLayout(this.layout, false, width, height);
vv.paint(graphics);
}
public List<ParameterType> getParameterTypes() {
return new LinkedList<>();
}
@Override
public Component getExportComponent() {
return vv;
}
@Override
public String getExportName() {
return I18N.getMessage(I18N.getGUIBundle(), "gui.cards.result_view.graph_view.title");
}
@Override
public String getIdentifier() {
return null;
}
@Override
public String getExportIconName() {
return I18N.getGUIMessage("gui.cards.result_view.graph_view.icon");
}
/**
* Setup rendering for tree models.
*
* @param graphCreator
* the creator instance
*/
private void setupRendering(final GraphCreator<V, E> graphCreator) {
// ## edge layout ##
// EDGE SHAPE
int edgeShapeType = graphCreator.getEdgeShape();
switch (edgeShapeType) {
case GraphCreator.EDGE_SHAPE_LINE:
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<V, E>());
break;
case GraphCreator.EDGE_SHAPE_QUAD_CURVE:
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.QuadCurve<V, E>());
break;
case GraphCreator.EDGE_SHAPE_CUBIC_CURVE:
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.CubicCurve<V, E>());
break;
case GraphCreator.EDGE_SHAPE_BENT_LINE:
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.BentLine<V, E>());
break;
case GraphCreator.EDGE_SHAPE_WEDGE:
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Wedge<V, E>(5));
break;
default:
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<V, E>());
break;
}
// EDGE FONT
vv.getRenderContext().setEdgeFontTransformer(new Transformer<E, Font>() {
@Override
public Font transform(E edge) {
if (graphCreator.isEdgeOnSelectedPath(vv.getPickedVertexState().getPicked(), edge)) {
return EDGE_FONT_ON_PATH;
} else {
return EDGE_FONT;
}
}
});
// EDGE COLORS
vv.getRenderContext().setEdgeDrawPaintTransformer(new Transformer<E, Paint>() {
@Override
public Paint transform(E edge) {
if (graphCreator.isEdgeOnSelectedPath(vv.getPickedVertexState().getPicked(), edge)) {
return NODE_ON_PATH;
} else {
return EDGE_LIGHT_GRAY;
}
}
});
vv.getRenderContext().setArrowDrawPaintTransformer(new Transformer<E, Paint>() {
@Override
public Paint transform(E edge) {
if (graphCreator.isEdgeOnSelectedPath(vv.getPickedVertexState().getPicked(), edge)) {
return NODE_ON_PATH;
} else {
return EDGE_LIGHT_GRAY;
}
}
});
vv.getRenderContext().setArrowFillPaintTransformer(new Transformer<E, Paint>() {
@Override
public Paint transform(E edge) {
if (graphCreator.isEdgeOnSelectedPath(vv.getPickedVertexState().getPicked(), edge)) {
return NODE_ON_PATH;
} else {
return EDGE_LIGHT_GRAY;
}
}
});
vv.getRenderContext().setEdgeArrowTransformer(new DirectionalEdgeArrowTransformer<V, E>(8, 7, 0));
// only change edge thickness for tree models for now
if (graphCreator instanceof TreeModelGraphCreator) {
vv.getRenderContext().setEdgeStrokeTransformer(new Transformer<E, Stroke>() {
@Override
public Stroke transform(E edge) {
float edgeStrength = Math.max(0.4f, 10f * (float) graphCreator.getEdgeStrength(edge));
return new BasicStroke(edgeStrength);
}
});
int length = 8;
int width = 7;
int notch = 0;
vv.getRenderContext().setEdgeArrowTransformer(new DirectionalEdgeArrowTransformer<V, E>(length, width, notch) {
@Override
public Shape transform(Context<Graph<V, E>, E> context) {
// yes, the order is correct and differs from the transformer constructor order
float edgeStrength = (float) graphCreator.getEdgeStrength(context.element);
float arrowWidth = Math.max(7f, 24f * edgeStrength);
float arrowLength = Math.max(8f, 16f * edgeStrength);
return ArrowFactory.getNotchedArrow(arrowWidth, arrowLength, notch);
}
});
}
// EDGE LABEL POSITION
vv.getRenderContext()
.setEdgeLabelClosenessTransformer(new ConstantDirectionalEdgeValueTransformer<V, E>(0.5d, 0.5d));
int labelOffset = graphCreator.getLabelOffset();
if (labelOffset >= 0) {
vv.getRenderContext().setLabelOffset(labelOffset);
}
// EDGE LABELS
vv.getRenderContext().setEdgeLabelTransformer(new Transformer<E, String>() {
@Override
public String transform(E object) {
return graphCreator.getEdgeName(object);
}
});
// EDGE LABEL RENDERER
Renderer.EdgeLabel<V, E> edgeLabelRenderer = graphCreator.getEdgeLabelRenderer();
if (edgeLabelRenderer != null) {
vv.getRenderer().setEdgeLabelRenderer(edgeLabelRenderer); // renderer...
}
vv.getRenderContext().setEdgeLabelRenderer(new EdgeLabelRenderer() { // ...context!
private JLabel renderer = new JLabel();
@Override
public <T> Component getEdgeLabelRendererComponent(JComponent parent, Object value, Font font,
boolean isSelected, T edge) {
renderer.setFont(font);
if (graphCreator.isEdgeLabelDecorating()) {
renderer.setOpaque(false);
renderer.setBackground(new Color(0, 0, 0, 0));
}
renderer.setText(value.toString());
return renderer;
}
/** Let the graph model decide. */
@Override
public boolean isRotateEdgeLabels() {
return graphCreator.isRotatingEdgeLabels();
}
/** Does nothing. */
@Override
public void setRotateEdgeLabels(boolean rotate) {}
});
// ## vertex layout ##
// VERTEX FONT
vv.getRenderContext().setVertexFontTransformer(new Transformer<V, Font>() {
@Override
public Font transform(V vertex) {
if (graphCreator.isBold(vertex)
|| graphCreator.isVertexOnSelectedPath(vv.getPickedVertexState().getPicked(), vertex)) {
return VERTEX_BOLD_FONT;
} else {
return VERTEX_PLAIN_FONT;
}
}
});
// VERTEX NAME
vv.getRenderContext().setVertexLabelTransformer(new Transformer<V, String>() {
@Override
public String transform(V object) {
return graphCreator.getVertexName(object);
}
});
// VERTEX FILL PAINT
Transformer<V, Paint> paintTransformer = graphCreator.getVertexPaintTransformer(vv);
if (paintTransformer == null) {
paintTransformer = new Transformer<V, Paint>() {
@Override
public Paint transform(V vertex) {
if (vv.getPickedVertexState().isPicked(vertex)) {
return NODE_SELECTED;
} else if (graphCreator.isVertexOnSelectedPath(vv.getPickedVertexState().getPicked(), vertex)) {
return NODE_ON_PATH;
} else {
if (graphCreator.isLeaf(vertex)) {
return LEAF_BACKGROUND;
} else {
return NODE_BACKGROUND;
}
}
}
};
}
vv.getRenderContext().setVertexFillPaintTransformer(paintTransformer);
// VERTEX DRAW PAINT
vv.getRenderContext().setVertexDrawPaintTransformer(new Transformer<V, Paint>() {
@Override
public Paint transform(V vertex) {
if (vv.getPickedVertexState().isPicked(vertex)) {
if (graphCreator.isLeaf(vertex)) {
return BORDER_INVIS;
} else {
return BORDER_INVIS;
}
} else {
if (graphCreator.isLeaf(vertex)) {
return BORDER_INVIS;
} else {
return BORDER_INVIS;
}
}
}
});
// VERTEX TOOL TIP
this.vv.setVertexToolTipTransformer(new Transformer<V, String>() {
@Override
public String transform(V vertex) {
return graphCreator.getVertexToolTip(vertex);
}
});
// VERTEX SHAPE
vv.getRenderContext().setVertexShapeTransformer(new ExtendedVertexShapeTransformer<>(graphCreator));
// VERTEX RENDERER
Renderer.Vertex<V, E> vertexRenderer = graphCreator.getVertexRenderer();
if (vertexRenderer != null) {
vv.getRenderer().setVertexRenderer(vertexRenderer);
}
// VERTEX LABEL RENDERER
setDefaultLabelPosition();
// custom renderer?
Renderer.VertexLabel<V, E> customVertexLabelRenderer = graphCreator.getVertexLabelRenderer();
if (customVertexLabelRenderer != null) {
vv.getRenderer().setVertexLabelRenderer(customVertexLabelRenderer);
}
// context
vv.getRenderContext().setVertexLabelRenderer(new VertexLabelRenderer() {
private JLabel label = new JLabel();
@Override
public <T> Component getVertexLabelRendererComponent(JComponent parent, Object object, Font font,
boolean isSelection, T vertex) {
label.setFont(font);
if (object != null) {
label.setText(object.toString());
} else {
label.setText("");
}
return label;
}
});
}
}