Search code examples
javaswinguser-interfacegraphspanning-tree

Graphically display the shortest path between two vertices in a graph


I wrote a program that collects at least 500 Wikipedia pages and links from these pages to other Wikipedia pages. It collects word frequencies from each webpage in order to determine the cost between two vertices(webpages) in a graph.

I am now trying to create a second program. I'm creating a GUI in NeatBeans using Swing where the user can select two webpages and then graphically see the shortest path between them. I am not sure how to visually display the graph. I have been looking into using JFreeChart or JUNG but neither seem to be capable of creating the kind of graph that I want.

Here is an example of what I am trying to create:

enter image description here


Solution

  • I made something using only custom painting. If you do not know how to use cutom painting, the tutorial is a good place to start, see Performing Custom Painting and 2D Graphics.

    enter image description here

    public class Example extends JPanel {
    
        final static int GRID_SIZE = 4;
        final static int CELL_SIZE = 150;
        final static int CIRCLE_RAD = 20;
        final static int HALF_CIRCLE_WIDTH = 4;
    
        final static Color BACKGROUND = Color.WHITE;
        final static Color CIRCLE_FILL = new Color(185, 42, 42);
        final static Color CIRCLE_EDGE = Color.BLACK;
        final static Color LINK_FILL = Color.BLUE;
        final static Color NOLINK_FILL = new Color(150, 75, 0);
    
        final static BasicStroke LINK_STROKE = new BasicStroke(10);
        final static BasicStroke NOLINK_STROKE = new BasicStroke(2);
        final static BasicStroke CIRCLE_STROKE = new BasicStroke(HALF_CIRCLE_WIDTH);
    
    
        boolean[][][] links = new boolean[GRID_SIZE][GRID_SIZE][2];
    
        Example() {
    
            setBackground(BACKGROUND);
    
            Random rand = new Random();
            for (int i = 0; i < GRID_SIZE; i++) {
                for (int j = 0; j < GRID_SIZE; j++) {
                    links[i][j][0] = rand.nextBoolean(); // isLinkingtoRight
                    links[i][j][1] = rand.nextBoolean(); // isLinkingtoDown
                }
            }
        }
    
        @Override
        public Dimension getPreferredSize() {
    
            int dim = CIRCLE_RAD * 2 + CELL_SIZE * (GRID_SIZE - 1) + HALF_CIRCLE_WIDTH * 2;
            return new Dimension(dim, dim);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
    
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
    
            for (int i = 0; i < GRID_SIZE; i++) {
                for (int j = 0; j < GRID_SIZE; j++) {
    
                    if (j != GRID_SIZE - 1) {
                        if (links[i][j][0]) {
                            g2d.setColor(LINK_FILL);
                            g2d.setStroke(LINK_STROKE);
                        }
                        else {
                            g2d.setColor(NOLINK_FILL);
                            g2d.setStroke(NOLINK_STROKE);
                        }
                        g2d.drawLine(i * CELL_SIZE + CIRCLE_RAD + HALF_CIRCLE_WIDTH, j * CELL_SIZE  + CIRCLE_RAD + HALF_CIRCLE_WIDTH,
                                     i * CELL_SIZE + CIRCLE_RAD + HALF_CIRCLE_WIDTH, (j + 1) * CELL_SIZE + CIRCLE_RAD + HALF_CIRCLE_WIDTH);
                    }
                    if (i != GRID_SIZE - 1) {
                        if (links[i][j][1]) {
                            g2d.setColor(LINK_FILL);
                            g2d.setStroke(LINK_STROKE);
                        }
                        else {
                            g2d.setColor(NOLINK_FILL);
                            g2d.setStroke(NOLINK_STROKE);
                        }
                        g2d.drawLine(i * CELL_SIZE + CIRCLE_RAD + HALF_CIRCLE_WIDTH, j * CELL_SIZE + CIRCLE_RAD + HALF_CIRCLE_WIDTH,
                                     (i + 1) * CELL_SIZE + CIRCLE_RAD + HALF_CIRCLE_WIDTH, j * CELL_SIZE + CIRCLE_RAD + HALF_CIRCLE_WIDTH);
                    }
                }
            }
    
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setStroke(CIRCLE_STROKE);
            for (int i = 0; i < GRID_SIZE; i++) {
                for (int j = 0; j < GRID_SIZE; j++) {
                    g2d.setColor(CIRCLE_FILL);
                    g2d.fillOval(i * CELL_SIZE + HALF_CIRCLE_WIDTH, j * CELL_SIZE + HALF_CIRCLE_WIDTH, CIRCLE_RAD * 2, CIRCLE_RAD * 2);
                    g2d.setColor(CIRCLE_EDGE);
                    g2d.drawOval(i * CELL_SIZE + HALF_CIRCLE_WIDTH, j * CELL_SIZE + HALF_CIRCLE_WIDTH, CIRCLE_RAD * 2, CIRCLE_RAD * 2);
                }
            }
        }
    
        public static void main(String[] args) {
    
            JFrame frame = new JFrame();
            frame.add(new Example());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
        }
    }
    

    Notes:

    • It is by no means an efficient (performance-wise) painting method. The loops runs twice, I turned on anti-aliasing etc.
    • The pixel calculations could be off (positions, sizes...).
    • The styles (colors, sizes...) are at your discretion.
    • The link grid is a simple but stupid implementation.