/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* PeerfactSim.KOM 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PeerfactSim.KOM. If not, see .
*
*/
package de.tud.kom.p2psim.impl.topology.views.visualization.world;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import de.tud.kom.p2psim.api.common.SimHost;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisNodeInformation;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.VisualizationComponent;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Host;
import de.tudarmstadt.maki.simonstrator.api.Oracle;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
import de.tudarmstadt.maki.simonstrator.api.component.HostComponent;
import de.tudarmstadt.maki.simonstrator.api.component.overlay.NodeInformation;
/**
* Generic component that visualizes information from nodes implementing the
* {@link NodeInformation} interface.
*
* @author Bjoern Richerzhagen
* @version 1.0, Jul 9, 2015
*/
public class NodeInfoComponentVis extends JComponent
implements VisualizationComponent {
protected Collection nodes = new LinkedList<>();
private JMenu menu = new JMenu("Node Info");
protected boolean[] activeLayers = new boolean[0];
boolean hideInactiveNodes = false;
private final String name;
public NodeInfoComponentVis(
final Class componentClass) {
setBounds(0, 0, VisualizationInjector.getWorldX(),
VisualizationInjector.getWorldY());
setOpaque(true);
setVisible(true);
this.name = componentClass.getSimpleName();
menu.setText("Info: "+name);
Event.scheduleWithDelay(1 * Time.MICROSECOND, new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
for (Host host : Oracle.getAllHosts()) {
try {
HostComponent c = host.getComponent(componentClass);
if (c instanceof NodeInformation) {
nodes.add(new NodeVis(host, (NodeInformation) c));
}
} catch (ComponentNotAvailableException e) {
// don't care
}
}
initializeMenu();
}
}, null, 0);
}
/**
* Hide rings for inactive nodes.
* @param hideOfflineNodes
*/
public void setHideInactiveNodes(boolean hideInactiveNodes) {
this.hideInactiveNodes = hideInactiveNodes;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
boolean first = true;
for (NodeVis vis : new ArrayList<>(nodes)) {
vis.draw(g2);
if (first) {
first = false;
vis.drawLegend(g2);
}
}
}
protected void initializeMenu() {
for (NodeVis vis : nodes) {
String[] dimensions = vis.nodeInfo
.getNodeColorDimensionDescriptions();
activeLayers = new boolean[dimensions.length];
for (int dim = 0; dim < dimensions.length; dim++) {
JCheckBoxMenuItem item = new JCheckBoxMenuItem(
dimensions[dim]);
item.setSelected(true);
item.addChangeListener(new ChangeListenerImpl(dim));
menu.add(item);
activeLayers[dim] = true;
}
break;
}
}
private class ChangeListenerImpl implements ChangeListener {
private final int dim;
public ChangeListenerImpl(int dim) {
this.dim = dim;
}
@Override
public void stateChanged(ChangeEvent e) {
activeLayers[dim] = !activeLayers[dim];
}
}
/**
* Visualization-fragments for Node-centric visualiation-information.
*
* @author Bjoern Richerzhagen
* @version 1.0, Sep 22, 2013
*/
private class NodeVis {
public final VisNodeInformation visNodeInfo;
public final NodeInformation nodeInfo;
private final SimHost host;
private final PositionVector loc;
private final Color activeGreen = new Color(0, 200, 0);
private final Color[] baseColors = { Color.ORANGE, Color.BLUE, Color.RED,
Color.PINK, Color.GRAY, Color.GREEN, Color.CYAN,
Color.WHITE };
private final Color[] tuColors = {
new Color(93, 133, 195), // 1a
new Color(80, 182, 149), // 3a
// new Color(221,223,72), // 5a
new Color(248,186,60), // 7a
new Color(233,80,62), // 9a
new Color(128, 69, 151), // 11a
new Color(0, 78, 138), // 1c
new Color(0, 136, 119), // 3c
// new Color(177, 189, 0), // 5c
new Color(210, 135, 0), // 7c
new Color(185, 15, 34), // 9c
new Color(97, 28, 115), // 11c
};
private Color[][] colors = null;
public NodeVis(Host host, NodeInformation nodeInfo) {
this.nodeInfo = nodeInfo;
this.host = (SimHost) host;
this.loc = this.host.getTopologyComponent().getRealPosition();
this.visNodeInfo = VisualizationInjector.getNodeInformation(host.getId());
/*
* Create per-info-option colors by deriving the color from the base color
*/
colors = new Color[nodeInfo.getNodeColorDimensions()][];
for (int dim = 0; dim < colors.length; dim++) {
Color baseColor = baseColors[dim];
int dimensionSize = nodeInfo
.getNodeColorDescriptions(dim).length;
colors[dim] = new Color[dimensionSize];
/*
* http://stackoverflow.com/questions/2355157/dynamically-
* creating-colors-with-different-brightness
*/
// float hsbVals[] = Color.RGBtoHSB(baseColor.getRed(),
// baseColor.getGreen(), baseColor.getBlue(), null);
for (int i = 0; i < dimensionSize; i++) {
float hue = i / (float) dimensionSize;
// colors[dim][i] = Color.getHSBColor(hue, hsbVals[1],
// hsbVals[2]);
colors[dim][i] = tuColors[i];
}
}
}
/**
* Called on one of the nodes to draw global objects such as a legend.
* Called before draw.
*
* @param g2
*/
public void drawLegend(Graphics2D g2) {
String[] dimensions = nodeInfo.getNodeColorDimensionDescriptions();
int segments = dimensions.length;
int segmentDegrees = (int) (360 / (double) segments);
int arcSize = 8;
g2.setStroke(new BasicStroke(3));
for (int color = 0; color < segments; color++) {
if (activeLayers.length <= color || !activeLayers[color]) {
continue;
}
g2.setColor(Color.DARK_GRAY);
g2.drawArc(10, 20 * (color + 1)+10, 2*arcSize, 2*arcSize, color*segmentDegrees, segmentDegrees);
String[] colorDescs = nodeInfo.getNodeColorDescriptions(color);
for (int i = 0; i < colorDescs.length; i++) {
g2.setColor(colors[color][i]);
g2.fillRect(40 + i * 90, 20 * (color + 1)+10, 8, 8);
g2.setColor(Color.DARK_GRAY);
g2.drawString(colorDescs[i], 50 + i * 90,
20 * (color + 2));
}
}
g2.setStroke(new BasicStroke(1));
}
public void draw(Graphics2D g2) {
if (hideInactiveNodes && !nodeInfo.isActive()) {
visNodeInfo.disableClickListener = true;
return;
}
visNodeInfo.disableClickListener = false;
Point center = loc.asPoint();
// Draw active (green) over underlay vis.
g2.setColor(nodeInfo.isActive() ? activeGreen : Color.LIGHT_GRAY);
int radius = 3;
g2.drawOval(VisualizationInjector.scaleValue(center.x) - radius, VisualizationInjector.scaleValue(center.y) - radius, radius * 2,
radius * 2);
if (!nodeInfo.isActive()) {
return;
}
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1.0f));
int segments = nodeInfo.getNodeColorDimensions();
int segmentDegrees = (int) (360 / (double) segments);
int arcSize = 8;
g2.setStroke(new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
for (int dim = 0; dim < segments; dim++) {
int value = nodeInfo.getNodeColor(dim);
if (value < 0 || !activeLayers[dim]) {
continue;
}
g2.setColor(colors[dim][value]);
g2.drawArc(VisualizationInjector.scaleValue(center.x)-arcSize, VisualizationInjector.scaleValue(center.y)-arcSize, 2*arcSize, 2*arcSize, dim*segmentDegrees, segmentDegrees);
}
g2.setStroke(new BasicStroke(1));
String nodeDesc = nodeInfo.getNodeDescription();
g2.drawString(nodeDesc, center.x + 4, center.y + 4);
}
}
@Override
public JComponent getComponent() {
return this;
}
@Override
public JMenu getCustomMenu() {
return menu;
}
@Override
public boolean isHidden() {
return false;
}
@Override
public String getDisplayName() {
return "Info: "+name;
}
}