Java,荣格框架:如何编辑和设置顶点位置

Java, Jung Framework: How to edit and set vertices locations

我正在尝试设置给定位置的顶点坐标(x 和 y)。我使用 VertexImageShaperDemo 示例。每个顶点的 x,y 坐标已经在数据库中。如何将所有 IP 地址放在它们的特定位置,即正方形(见图)?

这是我正在处理的代码:

public class VertexImageShaperDemo extends JApplet {

    private static final long serialVersionUID = -4332663871914930864L;

    private static int VERTEX_COUNT ;

    Map<Number,Double> mapX = new HashMap<Number,Double>();
    Map<Number,Double> mapY = new HashMap<Number,Double>();

    /**
     * the graph
     */
    DirectedSparseGraph<Number, Number> graph;


    //edu.uci.ics.jung.graph
    /**
     * the visual component and renderer for the graph
     */
    VisualizationViewer<Number, Number> vv;

    /**
     * some icon names to use
     */
    // String[] iconNames ;
    // ArrayList<String> iconNames = new ArrayList<String>();

    public VertexImageShaperDemo(JFrame frm) {

        try{
            String pilote = "com.mysql.jdbc.Driver";
            Class.forName(pilote);
            Connection connexion = DriverManager.getConnection("jdbc:mysql://localhost:3306/topology","root","");
            Statement instruction = connexion.createStatement();
            ResultSet resultat = instruction.executeQuery("select count(*) from node");
            resultat.next();
            int rowcount = resultat.getInt(1);
            VERTEX_COUNT = rowcount;
        }
        catch (Exception e1){
            System.out.println("echec pilote VERTEX_COUNT : "+e1);
        }

        // Create a simple graph for the demo
        graph = new DirectedSparseGraph<Number,Number>();
        createGraph(VERTEX_COUNT);

        // A Map for the labels
        Map<Number,String> map = new HashMap<Number,String>();
        /*
            for(int i=1; i<=VERTEX_COUNT; i++) {
                map.put(i, iconNames[i%iconNames.length]);
            }
        */

        try{
            String pilote = "com.mysql.jdbc.Driver";
            Class.forName(pilote);
            Connection connexion = DriverManager.getConnection("jdbc:mysql://localhost:3306/topology","root","");
            Statement instruction = connexion.createStatement();

            ResultSet resultat_Nodes = instruction.executeQuery("select * from node");

            while(resultat_Nodes.next()){
                int Node_ID =resultat_Nodes.getInt("Node_ID");
                String Host_name = resultat_Nodes.getString("Host_name");
                int OL_Bit =resultat_Nodes.getInt("OL_Bit");
                String TE_rtr_ID = resultat_Nodes.getString("TE_rtr_ID");
                double X = resultat_Nodes.getDouble("X");
                double Y = resultat_Nodes.getDouble("Y");
                //iconNames[Node_ID] = Host_name;
                map.put(Node_ID, TE_rtr_ID);
                mapX.put(Node_ID, X);
                mapY.put(Node_ID, Y);
            }

        }
        catch (Exception e1){
                System.out.println("echec pilote Nodes : "+e1);
        }

        // A Map for the Icons
        final FRLayout <Number, Number> layout = new FRLayout <Number, Number>(graph);
        //layout.setMaxIterations(100);
        // layout.setInitializer(new RandomLocationTransformer<Number>(new Dimension(400,400), 0));

        vv =  new VisualizationViewer<Number, Number>(layout, new Dimension(400,400));

        // This demo uses a special renderer to turn outlines on and off.
        // you do not need to do this in a real application.
        // Instead, just let vv use the Renderer it already has
        vv.getRenderer().setVertexRenderer(new DemoRenderer<Number,Number>());

        //PickedState<String> ps = vv.setPickedVertexState();

        //Renderer.VertexLabel.Position position =
        //(Renderer.VertexLabel.Position)e.getItem();
        //vv.getRenderer().getVertexLabelRenderer().setPosition(position);
        //vv.repaint();

        Transformer<Number,Paint> vpf = new PickableVertexPaintTransformer<Number>(vv.getPickedVertexState(), Color.white, Color.yellow);

        vv.getRenderContext().setVertexFillPaintTransformer(vpf);
        vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<Number>(vv.getPickedEdgeState(), Color.black, Color.cyan));

        vv.setBackground(Color.white);

        final Transformer<Number,String> vertexStringerImpl = new VertexStringerImpl<Number,String>(map);
        vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl);
        vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
        vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.green));

        // vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<Integer,String>());
        vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
        vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan));

        vv.getRenderContext().setEdgeLabelTransformer(new Transformer<Number,String>() {

            public String transform(Number input) {

                return input.toString();
            }
        });

        // For this demo only, I use a special class that lets me turn various
        // features on and off. For a real application, use VertexIconShapeTransformer instead.
        final DemoVertexIconShapeTransformer<Number> vertexIconShapeTransformer =
            new DemoVertexIconShapeTransformer<Number>(new EllipseVertexShapeTransformer<Number>());

        final DemoVertexIconTransformer<Number> vertexIconTransformer =
            new DemoVertexIconTransformer<Number>();

        vv.getRenderContext().setVertexShapeTransformer(vertexIconShapeTransformer);
        vv.getRenderContext().setVertexIconTransformer(vertexIconTransformer);

        /********************************************************************************************************/
        /*
        JFrame frame = new JFrame("Editing and Mouse Menu Demo");
        SparseMultigraph<GraphElements.MyVertex, GraphElements.MyEdge> g = new SparseMultigraph<GraphElements.MyVertex, GraphElements.MyEdge>();

        // Layout<V, E>, VisualizationViewer<V,E>
        //Map<GraphElements.MyVertex,Point2D> vertexLocations = new HashMap<GraphElements.MyVertex, Point2D>();
        Layout<GraphElements.MyVertex, GraphElements.MyEdge> layout = new StaticLayout(g);
        layout.setSize(new Dimension(300,300));
        VisualizationViewer<GraphElements.MyVertex,GraphElements.MyEdge> vv = new VisualizationViewer<GraphElements.MyVertex,GraphElements.MyEdge>(layout);
        vv.setPreferredSize(new Dimension(350,350));


        // Show vertex and edge labels
        vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());


        // Create a graph mouse and add it to the visualization viewer
        EditingModalGraphMouse gm = new EditingModalGraphMouse(vv.getRenderContext(), GraphElements.MyVertexFactory.getInstance(),GraphElements.MyEdgeFactory.getInstance());
        // Set some defaults for the Edges...
        GraphElements.MyEdgeFactory.setDefaultCapacity(192.0);
        GraphElements.MyEdgeFactory.setDefaultWeight(5.0);
        // Trying out our new popup menu mouse plugin...
        PopupVertexEdgeMenuMousePlugin myPlugin = new PopupVertexEdgeMenuMousePlugin();
        // Add some popup menus for the edges and vertices to our mouse plugin.
        JPopupMenu edgeMenu = new MyMouseMenus.EdgeMenu(frame);
        JPopupMenu vertexMenu = new MyMouseMenus.VertexMenu();
        myPlugin.setEdgePopup(edgeMenu);
        myPlugin.setVertexPopup(vertexMenu);
        gm.remove(gm.getPopupEditingPlugin());  // Removes the existing popup editing plugin

        gm.add(myPlugin);   // Add our new plugin to the mouse

        vv.setGraphMouse(gm);


        //JFrame frame = new JFrame("Editing and Mouse Menu Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(vv);

        // Let's add a menu for changing mouse modes
        JMenuBar menuBar = new JMenuBar();
        JMenu modeMenu = gm.getModeMenu();
        modeMenu.setText("Mouse Mode");
        modeMenu.setIcon(null); // I'm using this in a main menu
        modeMenu.setPreferredSize(new Dimension(80,20)); // Change the size so I can see the text

        menuBar.add(modeMenu);
        frame.setJMenuBar(menuBar);
        gm.setMode(ModalGraphMouse.Mode.EDITING); // Start off in editing mode
        frame.pack();
        frame.setVisible(true);
        */
        /***********************************************************************************************************/
        // un-comment for RStar Tree visual testing
        //vv.addPostRenderPaintable(new BoundingRectanglePaintable(vv.getRenderContext(), vv.getGraphLayout()));

        // Get the pickedState and add a listener that will decorate the
        // Vertex images with a checkmark icon when they are picked
        //PickedState<Number> ps = vv.getPickedVertexState();
        //ps.addItemListener(new PickWithIconListener<Number>(vertexIconTransformer));

        // add a listener for ToolTips
        vv.setVertexToolTipTransformer(new ToStringLabeller<Number>());
        /*
        vv.getGraphLayout().transform(1).setLocation(mapX.get(1), mapY.get(1));
        vv.getGraphLayout().transform(2).setLocation(mapX.get(2), mapY.get(2));
        vv.getGraphLayout().transform(3).setLocation(mapX.get(3), mapY.get(3));
        vv.getGraphLayout().transform(4).setLocation(mapX.get(4), mapY.get(4));
        vv.getGraphLayout().transform(5).setLocation(mapX.get(5), mapY.get(5));
        vv.getGraphLayout().transform(6).setLocation(mapX.get(6), mapY.get(6));
        vv.getGraphLayout().transform(7).setLocation(mapX.get(7), mapY.get(7));
        vv.getGraphLayout().transform(8).setLocation(mapX.get(8), mapY.get(8));
        */

        Container content = getContentPane();
        final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
        content.add(panel);

        final DefaultModalGraphMouse<Number,Number> graphMouse = new DefaultModalGraphMouse<Number,Number>();
        // final EditingModalGraphMouse graphMouse = new EditingModalGraphMouse(vv.getRenderContext(), GraphElements.MyVertexFactory.getInstance(),GraphElements.MyEdgeFactory.getInstance());


        /***********************************************************/
        /*
        // Set some defaults for the Edges...
        GraphElements.MyEdgeFactory.setDefaultCapacity(192.0);
        GraphElements.MyEdgeFactory.setDefaultWeight(5.0);
        // Trying out our new popup menu mouse plugin...
        PopupVertexEdgeMenuMousePlugin myPlugin = new PopupVertexEdgeMenuMousePlugin();
        // Add some popup menus for the edges and vertices to our mouse plugin.
        JPopupMenu edgeMenu = new MyMouseMenus.EdgeMenu(frm);
        JPopupMenu vertexMenu = new MyMouseMenus.VertexMenu();
        myPlugin.setEdgePopup(edgeMenu);
        myPlugin.setVertexPopup(vertexMenu);
        graphMouse.remove(graphMouse.getPopupEditingPlugin());  // Removes the existing popup editing plugin

        graphMouse.add(myPlugin);   // Add our new plugin to the mouse
        */
        /***********************************************************/


        vv.setGraphMouse(graphMouse);
        vv.addKeyListener(graphMouse.getModeKeyListener());
        final ScalingControl scaler = new CrossoverScalingControl();

        JButton plus = new JButton("+");
        plus.addActionListener(new ActionListener() {
            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());
            }
        });

        JButton XYButton = new JButton("Save X & Y coordiantes");
        XYButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                try{
                    String pilote = "com.mysql.jdbc.Driver";
                    Class.forName(pilote);
                    Connection connexion = DriverManager.getConnection("jdbc:mysql://localhost:3306/topology","root","");
                    Statement instruction = connexion.createStatement();

                    for (int i = 1; i <= VERTEX_COUNT; i++) {
                        mapX.put(i, layout.getX(i));
                        mapY.put(i, layout.getY(i));

                        instruction.executeUpdate("Update node set X =" + layout.getX(i) + ", Y="+ layout.getY(i) +"where Node_ID = " + i +"");
                    }
                }
                catch (Exception e1){
                    System.out.println("echec pilote : "+e1);
                }
            }
        });

        JCheckBox shape = new JCheckBox("Shape");
        shape.addItemListener(new ItemListener(){

            public void itemStateChanged(ItemEvent e) {
                vertexIconShapeTransformer.setShapeImages(e.getStateChange()==ItemEvent.SELECTED);
                vv.repaint();
            }
        });
        shape.setSelected(false);

        JCheckBox fill = new JCheckBox("Fill");
        fill.addItemListener(new ItemListener(){

            public void itemStateChanged(ItemEvent e) {
                vertexIconTransformer.setFillImages(e.getStateChange()==ItemEvent.SELECTED);
                vv.repaint();
            }
        });
        fill.setSelected(false);

        JCheckBox drawOutlines = new JCheckBox("Outline");
        drawOutlines.addItemListener(new ItemListener(){

            public void itemStateChanged(ItemEvent e) {
                vertexIconTransformer.setOutlineImages(e.getStateChange()==ItemEvent.SELECTED);
                vv.repaint();
            }
        });
        drawOutlines.setSelected(true);

        JComboBox modeBox = graphMouse.getModeComboBox();
        JPanel modePanel = new JPanel();
        modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
        modePanel.add(modeBox);

        /*
        vv.setGraphMouse(graphMouse);
        vv.addKeyListener(graphMouse.getModeKeyListener());
        graphMouse.setMode(ModalGraphMouse.Mode.EDITING);
         **/

        JPanel scaleGrid = new JPanel(new GridLayout(1,0));
        scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));

        JPanel labelFeatures = new JPanel(new GridLayout(1,0));
        labelFeatures.setBorder(BorderFactory.createTitledBorder("Image Effects"));

        JPanel controls = new JPanel();
        controls.add(XYButton);

        scaleGrid.add(plus);
        scaleGrid.add(minus);

        controls.add(scaleGrid);

        /*
            labelFeatures.add(shape);
            labelFeatures.add(fill);
            labelFeatures.add(drawOutlines);

            controls.add(labelFeatures);
        */

        controls.add(modePanel);
        content.add(controls, BorderLayout.SOUTH);
    }

    /**
     * When Vertices are picked, add a checkmark icon to the imager.
     * Remove the icon when a Vertex is unpicked
     * @author Tom Nelson
     *
     */
    public static class PickWithIconListener<V> implements ItemListener {
        DefaultVertexIconTransformer<V> imager;
        Icon checked;

        public PickWithIconListener(DefaultVertexIconTransformer<V> imager) {
            this.imager = imager;
            checked = new Checkmark();
        }

        public void itemStateChanged(ItemEvent e) {
            Icon icon = imager.transform((V)e.getItem());
            if(icon != null && icon instanceof LayeredIcon) {
                if(e.getStateChange() == ItemEvent.SELECTED) {
                    ((LayeredIcon)icon).add(checked);
                } else {
                    ((LayeredIcon)icon).remove(checked);
                }
            }
        }
    }

    /**
     * A simple implementation of VertexStringer that
     * gets Vertex labels from a Map
     *
     * @author Tom Nelson
     *
     *
     */
    public static class VertexStringerImpl<V,S> implements Transformer<V,String> {
        /*
        vv.getGraphLayout().transform(1).setLocation(mapX.get(1), mapY.get(1));
        vv.getGraphLayout().transform(2).setLocation(mapX.get(2), mapY.get(2));
        vv.getGraphLayout().transform(3).setLocation(mapX.get(3), mapY.get(3));
        vv.getGraphLayout().transform(4).setLocation(mapX.get(4), mapY.get(4));
        vv.getGraphLayout().transform(5).setLocation(mapX.get(5), mapY.get(5));
        vv.getGraphLayout().transform(6).setLocation(mapX.get(6), mapY.get(6));
        vv.getGraphLayout().transform(7).setLocation(mapX.get(7), mapY.get(7));
        vv.getGraphLayout().transform(8).setLocation(mapX.get(8), mapY.get(8));
        */
        Map<V,String> map = new HashMap<V,String>();

        boolean enabled = true;

        public VertexStringerImpl(Map<V,String> map) {
            this.map = map;
        }

        /* (non-Javadoc)
         * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex)
         */
        public String transform(V v) {
            if(isEnabled()) {
                return map.get(v);
                //layout.transform(v).setLocation(x, y);
            } else {
                return "";
            }
        }

        /**
         * @return Returns the enabled.
         */
        public boolean isEnabled() {
            return enabled;
        }

        /**
         * @param enabled The enabled to set.
         */
        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    }

    /**
     * create some vertices
     * @param count how many to create
     * @return the Vertices in an array
     */
    private void createGraph(int vertexCount) {
        for (int i = 1; i <= vertexCount; i++) {
            graph.addVertex(i);
        }

        try{
            String pilote = "com.mysql.jdbc.Driver";
            Class.forName(pilote);
            Connection connexion = DriverManager.getConnection("jdbc:mysql://localhost:3306/topology","root","");
            Statement instruction = connexion.createStatement();
            ResultSet resultat_Links = instruction.executeQuery("select * from link");

            while(resultat_Links.next()){
                int Link_ID =resultat_Links.getInt("Link_ID");
                int Node_id =resultat_Links.getInt("Node_id");
                int remote_link_node_id =resultat_Links.getInt("remote_link_node_id");
                graph.addEdge(Link_ID, Node_id, remote_link_node_id, EdgeType.DIRECTED);
            }

        }
        catch (Exception e1){
            System.out.println("echec pilote links: " + e1);
        }
        /*
         * int j=1;
        graph.addEdge(j++, 0, 1, EdgeType.DIRECTED);
        graph.addEdge(j++, 3, 0, EdgeType.DIRECTED);
        graph.addEdge(j++, 0, 4, EdgeType.DIRECTED);
        graph.addEdge(j++, 4, 5, EdgeType.DIRECTED);
        graph.addEdge(j++, 5, 3, EdgeType.DIRECTED);
        graph.addEdge(j++, 2, 1, EdgeType.DIRECTED);
        graph.addEdge(j++, 4, 1, EdgeType.DIRECTED);
        graph.addEdge(j++, 8, 2, EdgeType.DIRECTED);
        graph.addEdge(j++, 3, 8, EdgeType.DIRECTED);
        graph.addEdge(j++, 6, 7, EdgeType.DIRECTED);
        graph.addEdge(j++, 7, 5, EdgeType.DIRECTED);
        graph.addEdge(j++, 0, 9, EdgeType.DIRECTED);
        graph.addEdge(j++, 9, 8, EdgeType.DIRECTED);
        graph.addEdge(j++, 7, 6, EdgeType.DIRECTED);
        graph.addEdge(j++, 6, 5, EdgeType.DIRECTED);
        graph.addEdge(j++, 4, 2, EdgeType.DIRECTED);
        graph.addEdge(j++, 5, 4, EdgeType.DIRECTED);
        graph.addEdge(j++, 4, 10, EdgeType.DIRECTED);
        graph.addEdge(j++, 10, 4, EdgeType.DIRECTED);
        */
    }

    /**
     * this class exists only to provide settings to turn on/off shapes and image fill
     * in this demo.
     * In a real application, use DefaultVertexIconTransformer instead.
     *
     */
    public static class DemoVertexIconTransformer<V> extends DefaultVertexIconTransformer<V>
        implements Transformer<V,Icon> {

        boolean fillImages = true;
        boolean outlineImages = false;

        /**
         * @return Returns the fillImages.
         */
        public boolean isFillImages() {
            return fillImages;
        }
        /**
         * @param fillImages The fillImages to set.
         */
        public void setFillImages(boolean fillImages) {
            this.fillImages = fillImages;
        }

        public boolean isOutlineImages() {
            return outlineImages;
        }
        public void setOutlineImages(boolean outlineImages) {
            this.outlineImages = outlineImages;
        }

        public Icon transform(V v) {
            if(fillImages) {
                return (Icon)iconMap.get(v);
            } else {
                return null;
            }
        }
    }

    /**
     * this class exists only to provide settings to turn on/off shapes and image fill
     * in this demo.
     * In a real application, use VertexIconShapeTransformer instead.
     *
     */
    public static class DemoVertexIconShapeTransformer<V> extends VertexIconShapeTransformer<V> {

        boolean shapeImages = true;

        public DemoVertexIconShapeTransformer(Transformer<V,Shape> delegate) {
            super(delegate);
        }

        /**
         * @return Returns the shapeImages.
         */
        public boolean isShapeImages() {
            return shapeImages;
        }

        /**
         * @param shapeImages The shapeImages to set.
         */
        public void setShapeImages(boolean shapeImages) {
            shapeMap.clear();
            this.shapeImages = shapeImages;
        }

        public Shape transform(V v) {
            //Icon icon = (Icon) iconMap.get(v);

            Icon icon = new LayeredIcon(new ImageIcon(VertexImageShaperDemo.class.getResource("/images/Router.png")).getImage());

            if (icon != null && icon instanceof ImageIcon) {

                Image image = ((ImageIcon) icon).getImage();

                Shape shape = shapeMap.get(image);
                if (shape == null) {
                    if (shapeImages) {
                        shape = FourPassImageShaper.getShape(image, 30);
                    }
                    else {
                        shape = new Rectangle2D.Float(0, 0, image.getWidth(null), image.getHeight(null));
                    }
                    if(shape.getBounds().getWidth() > 0 &&
                            shape.getBounds().getHeight() > 0) {
                        int width = image.getWidth(null);
                        int height = image.getHeight(null);
                        AffineTransform transform =
                            AffineTransform.getTranslateInstance(-width / 2, -height / 2);
                        shape = transform.createTransformedShape(shape);
                        shapeMap.put(image, shape);
                    }
                }
                return shape;
            }
            else {
                return delegate.transform(v);
            }
        }
    }

    /**
     * A special renderer that can turn outlines on and off
     * in this demo.
     * You won't need this for a real application.
     * Use BasicVertexRenderer instead
     *
     * @author Tom Nelson
     *
     */
    class DemoRenderer<V,E> extends BasicVertexRenderer<V,E> {
        public void paintIconForVertex(RenderContext<V,E> rc, V v, Layout<V,E> layout) {

            //layout.transform(v).setLocation(mapX.get(v), mapY.get(v));
            Point2D p = layout.transform(v);
            p = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p);
            //Shape shape = null;


            float x = (float)p.getX();
            float y = (float)p.getY();

            //double x = mapX.get(v);
            //double y = mapY.get(v);

            GraphicsDecorator g = rc.getGraphicsContext();
            boolean outlineImages = false;
            Transformer<V,Icon> vertexIconFunction = rc.getVertexIconTransformer();

            if(vertexIconFunction instanceof DemoVertexIconTransformer) {
                outlineImages = ((DemoVertexIconTransformer<V>)vertexIconFunction).isOutlineImages();
            }
            Icon icon = vertexIconFunction.transform(v);
            if(icon == null || outlineImages ) {

                if(mapX.isEmpty() && mapY.isEmpty()){
                    Shape s = AffineTransform.getTranslateInstance(mapX.get(v),mapY.get(v)).createTransformedShape(rc.getVertexShapeTransformer().transform(v).getBounds2D());
                    paintShapeForVertex(rc, v, s);
                }
                else{
                    Shape s = AffineTransform.getTranslateInstance(mapX.get(v),mapY.get(v)).createTransformedShape(rc.getVertexShapeTransformer().transform(v).getBounds2D());
                    paintShapeForVertex(rc, v, s);
                }



                /*
                int xLoc = (int) (x - 50/2);
                int yLoc = (int) (y - 50/2);
                shape = new Rectangle (xLoc, yLoc, 50, 50);
                paintShapeForVertex(rc, v, shape);
             */

            }
            if(icon != null) {
                int xLoc = (int) (mapX.get(v) - icon.getIconWidth()/2);
                int yLoc = (int) (mapY.get(v) - icon.getIconHeight()/2);
                icon.paintIcon(rc.getScreenDevice(), g.getDelegate(), xLoc, yLoc);
            }
        }
    }

    /**
     * A driver for this demo
     */
    public static void main(String[] args) {
        JFrame frame = new JFrame("Segment routing topology project");
        Container content = frame.getContentPane();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        content.add(new VertexImageShaperDemo(frame));
        frame.pack();
        frame.setVisible(true);
    }
}

StaticLayout class 可用于指定已知(而不是算法生成的)顶点位置;这是 Stack Overflow 上的一个常见 JUNG-related 问题:https://whosebug.com/search?q=staticlayout+jung