You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Floyd-Warshall算法结果的Swing有向图绘制问题求助

解决Swing有向图双向边重叠问题

我还没熟练掌握Swing的用法,希望输入顶点和各边权值后,能生成符合要求的有向图示意图。现在我的代码里,像<1,2>(权值8)和<2,1>(权值3)这种双向边会被画成直线,容易混淆,没法做出示例图那样的效果,附上我的代码:

import javax.swing.*;    
import java.awt.*;    
import java.awt.event.ActionEvent;    
import java.awt.event.ActionListener;
import java.awt.geom.QuadCurve2D;
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;

public class ShowGraph extends JFrame {
    private final int[][] graph;
    private final int[] xCoordinates;
    private final int[] yCoordinates;

    public ShowGraph(int[][] graph, int[] xCoordinates, int[] yCoordinates) {
        this.graph = graph;
        this.xCoordinates = xCoordinates;
        this.yCoordinates = yCoordinates;

        setTitle("Graph Illustration");
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setSize(500, 500);

        GraphPanel graphPanel = new GraphPanel();
        add(graphPanel);

        JButton closeButton = new JButton("Close");
        closeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        });
        add(closeButton, BorderLayout.SOUTH);

        setLocationRelativeTo(null);
        setVisible(true);
    }

    class GraphPanel extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int radius = 20;

            // 绘制顶点
            for (int i = 0; i < graph.length; i++) {
                int x = xCoordinates[i];
                int y = yCoordinates[i];

                g.setColor(Color.BLACK);
                g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
                // 调整顶点编号位置,居中显示
                FontMetrics fm = g.getFontMetrics();
                int textX = x - fm.stringWidth(String.valueOf(i + 1)) / 2;
                int textY = y + fm.getAscent() / 2;
                g.drawString(String.valueOf(i + 1), textX, textY);
            }

            Graphics2D g2d = (Graphics2D) g;
            g2d.setStroke(new BasicStroke(2));
            int offset = 30; // 双向边控制点偏移量

            // 绘制有向边(曲线)、箭头和权值
            for (int i = 0; i < graph.length; i++) {
                for (int j = 0; j < graph.length; j++) {
                    if (graph[i][j] != 0 && graph[i][j] != Integer.MAX_VALUE) {
                        int x1 = xCoordinates[i];
                        int y1 = yCoordinates[i];
                        int x2 = xCoordinates[j];
                        int y2 = yCoordinates[j];

                        QuadCurve2D curve;
                        int labelX, labelY;

                        // 判断是否是双向边(i<j避免重复处理)
                        boolean isBidirectional = (i != j) && graph[j][i] != 0 && graph[j][i] != Integer.MAX_VALUE;
                        if (isBidirectional && i < j) {
                            // 第一条边(i->j):控制点向上偏移
                            int ctrlX = (x1 + x2) / 2;
                            int ctrlY = (y1 + y2) / 2 - offset;
                            curve = new QuadCurve2D.Float(x1, y1, ctrlX, ctrlY, x2, y2);
                            // 权值标签放在曲线上方
                            labelX = ctrlX;
                            labelY = ctrlY - 10;
                        } else if (isBidirectional && i > j) {
                            // 第二条边(j->i):控制点向下偏移
                            int ctrlX = (x1 + x2) / 2;
                            int ctrlY = (y1 + y2) / 2 + offset;
                            curve = new QuadCurve2D.Float(x1, y1, ctrlX, ctrlY, x2, y2);
                            // 权值标签放在曲线下方
                            labelX = ctrlX;
                            labelY = ctrlY + 10;
                        } else {
                            // 单向边或自环,用默认控制点(稍微偏移避免和顶点重叠)
                            if (i == j) {
                                // 自环:绘制圆形曲线
                                int ctrlX = x1 + radius + offset;
                                int ctrlY = y1 - radius - offset;
                                curve = new QuadCurve2D.Float(x1, y1 - radius, ctrlX, ctrlY, x1, y1 + radius);
                                labelX = ctrlX;
                                labelY = ctrlY;
                            } else {
                                int ctrlX = (x1 + x2) / 2;
                                int ctrlY = (y1 + y2) / 2 - offset/2;
                                curve = new QuadCurve2D.Float(x1, y1, ctrlX, ctrlY, x2, y2);
                                labelX = ctrlX;
                                labelY = ctrlY - 10;
                            }
                        }

                        // 绘制曲线
                        g2d.setColor(Color.BLUE);
                        g2d.draw(curve);

                        // 绘制箭头(在终点)
                        drawArrow(g2d, (int) curve.getX2(), (int) curve.getY2(), (int) curve.getCtrlX(), (int) curve.getCtrlY());

                        // 绘制权值标签
                        g2d.setColor(Color.BLACK);
                        FontMetrics fm = g2d.getFontMetrics();
                        labelX -= fm.stringWidth(String.valueOf(graph[i][j])) / 2;
                        g2d.drawString(String.valueOf(graph[i][j]), labelX, labelY);
                    }
                }
            }
        }

        private void drawArrow(Graphics2D g, int endX, int endY, int ctrlX, int ctrlY) {
            // 计算从控制点到终点的方向角
            double angle = Math.atan2(endY - ctrlY, endX - ctrlX);
            int arrowSize = 10;
            // 箭头的两个端点
            int x1 = endX - (int) (arrowSize * Math.cos(angle) + arrowSize * Math.sin(angle));
            int y1 = endY - (int) (arrowSize * Math.sin(angle) - arrowSize * Math.cos(angle));
            int x2 = endX - (int) (arrowSize * Math.cos(angle) - arrowSize * Math.sin(angle));
            int y2 = endY - (int) (arrowSize * Math.sin(angle) + arrowSize * Math.cos(angle));

            g.setColor(Color.BLACK);
            g.drawLine(endX, endY, x1, y1);
            g.drawLine(endX, endY, x2, y2);
        }
    }
}

修改说明

  1. 移除重复绘制:删掉原代码中重复的直线绘制逻辑,统一用QuadCurve2D绘制曲线边,避免重叠混乱。
  2. 双向边偏移处理:判断双向边时,给两条边的控制点分别设置上下偏移,让两条曲线分开显示,不会重合。
  3. 优化顶点编号位置:用FontMetrics计算文本宽度,让顶点编号居中显示在圆圈内。
  4. 调整箭头绘制逻辑:只在边的终点绘制箭头,箭头方向根据曲线控制点到终点的方向计算,更符合有向边的指向。
  5. 权值标签适配:根据曲线位置调整权值标签的显示位置,避免被曲线遮挡。
  6. 新增自环处理:针对顶点到自身的边,绘制环形曲线,避免和顶点重叠。

内容的提问来源于stack exchange,提问作者jojo can't code

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:55:56