Java tutorial
/* * This program 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 * (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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package es.urjc.ia.fia.genericSearch.statistics; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.PolarPoint; import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout; import edu.uci.ics.jung.algorithms.layout.TreeLayout; import edu.uci.ics.jung.graph.DelegateTree; import edu.uci.ics.jung.visualization.GraphZoomScrollPane; import edu.uci.ics.jung.visualization.Layer; import edu.uci.ics.jung.visualization.VisualizationServer; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; import edu.uci.ics.jung.visualization.control.ModalGraphMouse; import edu.uci.ics.jung.visualization.control.ScalingControl; import edu.uci.ics.jung.visualization.decorators.EdgeShape; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.layout.LayoutTransition; import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position; import edu.uci.ics.jung.visualization.util.Animator; import es.urjc.ia.fia.genericSearch.data.Action; import es.urjc.ia.fia.genericSearch.data.State; import org.apache.commons.collections15.Transformer; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.util.*; import java.util.List; public class JUNGStatistics<ACTION extends Action> extends TextStatistics<ACTION> { class AuxVertex { private State<ACTION> state = null; private int added = 1; private List<Integer> explored = null; private boolean duplicated; public AuxVertex(int _added, State<ACTION> _state, boolean _duplicated) { this.state = _state; this.added = _added; this.explored = new ArrayList<Integer>(); this.duplicated = _duplicated; } /** * @return the state */ protected State<ACTION> getState() { return state; } /** * @return the n */ protected int getAdded() { return added; } /** * @return the explored */ public List<Integer> getExplored() { return this.explored; } /** * * @return */ public boolean addExplored(int _e) { return this.explored.add(_e); } /** * * @return */ public boolean isExplored() { return !this.explored.isEmpty(); } /** * @return the duplicated */ public boolean isDuplicated() { return duplicated; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { try { AuxVertex other = (AuxVertex) obj; return (other.getAdded() == this.getAdded()); } catch (Exception e) { return false; } } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return this.added; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { if (this.isExplored()) { if (this.getExplored().size() == 1) { return Integer.toString(this.getExplored().get(0)); } else { return this.getExplored().toString(); } } else { return ""; } } } /* * Visualization with JUNG */ private VisualizationViewer<AuxVertex, ACTION> vv = null; private Layout<AuxVertex, ACTION> treeLayout = null; private RadialTreeLayout<AuxVertex, ACTION> radialLayout = null; private Rings rings = null; private DelegateTree<AuxVertex, ACTION> tree = null; private Hashtable<State<ACTION>, AuxVertex> map = null; private int nnodesAdded = 1; private int nnodesExplored = 1; public JUNGStatistics() { this.tree = new DelegateTree<AuxVertex, ACTION>(); this.map = new Hashtable<State<ACTION>, AuxVertex>(); this.nnodesAdded = 1; this.nnodesExplored = 1; } @Override public void init() { super.init(); this.tree = new DelegateTree<AuxVertex, ACTION>(); this.map = new Hashtable<State<ACTION>, AuxVertex>(); this.nnodesAdded = 1; this.nnodesExplored = 1; } @Override public void end() { super.end(); } @Override public void addRootState(State<ACTION> _state) { super.addRootState(_state); if (!this.map.contains(_state)) { AuxVertex v = new AuxVertex(nnodesAdded++, _state, false); tree.addVertex(v); this.map.put(_state, v); } } @Override public void addState(ACTION _action, State<ACTION> _parent, State<ACTION> _child) { super.addState(_action, _parent, _child); // Expansion without number, if now in the statistics if (!this.map.contains(_child)) { AuxVertex parent = this.map.get(_parent); AuxVertex child = new AuxVertex(nnodesAdded++, _child, false); tree.addChild(_action, parent, child); this.map.put(_child, child); } } @Override public void addDuplicateState(ACTION _action, State<ACTION> _parent, State<ACTION> _child) { super.addDuplicateState(_action, _parent, _child); if (!this.map.contains(_child)) { AuxVertex parent = this.map.get(_parent); // Expansion without number AuxVertex child = new AuxVertex(nnodesAdded++, _child, true); tree.addChild(_action, parent, child); this.map.put(_child, child); } } @Override public void exploreState(State<ACTION> _state) { super.exploreState(_state); this.map.get(_state).addExplored(nnodesExplored++); } @Override public void showStatistics() { // Layout: tree and radial treeLayout = new TreeLayout<AuxVertex, ACTION>(this.tree, 35, 100); radialLayout = new RadialTreeLayout<AuxVertex, ACTION>(this.tree, 35, 130); radialLayout.setSize(new Dimension(200 * (this.tree.getHeight() + 1), 200 * (this.tree.getHeight() + 1))); // The BasicVisualizationServer<V,E> is parameterized by the edge types vv = new VisualizationViewer<AuxVertex, ACTION>(treeLayout); vv.setPreferredSize(new Dimension(800, 600)); //Sets the viewing area size vv.setBackground(Color.white); // Background color vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<AuxVertex>()); vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<AuxVertex, ACTION>()); // Setup up a new vertex to paint transformer Transformer<AuxVertex, Paint> vertexPaint = new Transformer<AuxVertex, Paint>() { @Override public Paint transform(AuxVertex arg0) { if (arg0.getState().isSolution() && arg0.isExplored()) { return Color.GREEN; } else if (arg0.isExplored()) { return Color.CYAN; } else if (arg0.isDuplicated()) { return Color.GRAY; } else { return Color.WHITE; } } }; // Tooltip for vertex Transformer<AuxVertex, String> toolTipsState = new Transformer<AuxVertex, String>() { @Override public String transform(AuxVertex arg0) { String sortInfo = ""; if (arg0.isExplored()) { if (arg0.getState().isSolution() && arg0.isExplored()) { sortInfo = "<u><i>Solution state " + arg0.getExplored() + "</i></u>"; } else { sortInfo = "<u><i>Explored state " + arg0.getExplored() + "</i></u>"; } } else if (arg0.isDuplicated()) { sortInfo = "<u><i>Duplicated state </i></u>"; } else { sortInfo = "<u><i>Unexplored state</i></u>"; } return "<html><p>" + sortInfo + "</p>" + "<p>State: " + arg0.getState().toString() + "</p>" + "<p>Cost: " + arg0.getState().getSolutionCost() + "</p></html>"; } }; vv.setVertexToolTipTransformer(toolTipsState); // Tooltip for edge Transformer<ACTION, String> toolTipsAction = new Transformer<ACTION, String>() { @Override public String transform(ACTION arg0) { return "Cost: " + arg0.cost(); } }; vv.setEdgeToolTipTransformer(toolTipsAction); vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<AuxVertex, ACTION>()); vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<ACTION>()); vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint); vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR); // Create a graph mouse and add it to the visualization component DefaultModalGraphMouse<State<ACTION>, ACTION> gm = new DefaultModalGraphMouse<State<ACTION>, ACTION>(); gm.setMode(ModalGraphMouse.Mode.TRANSFORMING); vv.setGraphMouse(gm); vv.addKeyListener(gm.getModeKeyListener()); JFrame vFrame = new JFrame("Statistics Tree View"); vFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); /* vFrame.getContentPane().add(vv); vFrame.pack(); vFrame.setVisible(true); */ rings = new Rings(); Container content = vFrame.getContentPane(); final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv); content.add(panel); JComboBox modeBox = gm.getModeComboBox(); modeBox.addItemListener(gm.getModeListener()); gm.setMode(ModalGraphMouse.Mode.TRANSFORMING); final ScalingControl scaler = new CrossoverScalingControl(); JButton plus = new JButton("+"); plus.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { scaler.scale(vv, 1.1f, vv.getCenter()); } }); JButton minus = new JButton("-"); minus.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { scaler.scale(vv, 1 / 1.1f, vv.getCenter()); } }); JToggleButton radial = new JToggleButton("Radial"); radial.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { LayoutTransition<AuxVertex, ACTION> lt = new LayoutTransition<AuxVertex, ACTION>(vv, treeLayout, radialLayout); Animator animator = new Animator(lt); animator.start(); vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); vv.addPreRenderPaintable(rings); } else { LayoutTransition<AuxVertex, ACTION> lt = new LayoutTransition<AuxVertex, ACTION>(vv, radialLayout, treeLayout); Animator animator = new Animator(lt); animator.start(); vv.getRenderContext().getMultiLayerTransformer().setToIdentity(); vv.removePreRenderPaintable(rings); } vv.repaint(); } }); JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); /* * Statistics */ JLabel stats = new JLabel(); long totalTime = endTime.getTime() - initTime.getTime(); stats.setText("<html>" + "<b>Total States:</b> " + this.totalStates + "<br>" + "<b>Explored States:</b> " + this.explorerStates + "<br>" + "<b>Duplicated States (detected):</b> " + this.duplicatedStates + "<br>" + "<b>Solution States:</b> " + this.solutionStates + "<br>" + "<b>Total time: </b>" + totalTime + " milliseconds" + "</html>"); JPanel legend = new JPanel(); legend.setLayout(new BoxLayout(legend, BoxLayout.Y_AXIS)); JLabel len = new JLabel("<html><b>Lengend</b></html>"); JLabel lexpl = new JLabel("Explored state"); lexpl.setBackground(Color.CYAN); lexpl.setOpaque(true); JLabel lune = new JLabel("Unexplored state"); lune.setBackground(Color.WHITE); lune.setOpaque(true); JLabel ldupl = new JLabel("Duplicated state"); ldupl.setBackground(Color.GRAY); ldupl.setOpaque(true); JLabel lsol = new JLabel("Solution state"); lsol.setBackground(Color.GREEN); lsol.setOpaque(true); legend.add(len); legend.add(lexpl); legend.add(lune); legend.add(ldupl); legend.add(lsol); JPanel controls = new JPanel(); controls.add(stats); scaleGrid.add(plus); scaleGrid.add(minus); controls.add(radial); controls.add(scaleGrid); //controls.add(modeBox); controls.add(legend); content.add(controls, BorderLayout.SOUTH); vFrame.pack(); vFrame.setVisible(true); } class Rings implements VisualizationServer.Paintable { Collection<Double> depths; public Rings() { depths = getDepths(); } private Collection<Double> getDepths() { Set<Double> depths = new HashSet<Double>(); Map<AuxVertex, PolarPoint> polarLocations = radialLayout.getPolarLocations(); for (AuxVertex v : tree.getVertices()) { PolarPoint pp = polarLocations.get(v); depths.add(pp.getRadius()); } return depths; } public void paint(Graphics g) { g.setColor(Color.lightGray); Graphics2D g2d = (Graphics2D) g; Point2D center = radialLayout.getCenter(); Ellipse2D ellipse = new Ellipse2D.Double(); for (double d : depths) { ellipse.setFrameFromDiagonal(center.getX() - d, center.getY() - d, center.getX() + d, center.getY() + d); Shape shape = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT) .transform(ellipse); g2d.draw(shape); } } public boolean useTransform() { return true; } } }