基于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); } } }
修改说明
- 移除重复绘制:删掉原代码中重复的直线绘制逻辑,统一用
QuadCurve2D绘制曲线边,避免重叠混乱。 - 双向边偏移处理:判断双向边时,给两条边的控制点分别设置上下偏移,让两条曲线分开显示,不会重合。
- 优化顶点编号位置:用
FontMetrics计算文本宽度,让顶点编号居中显示在圆圈内。 - 调整箭头绘制逻辑:只在边的终点绘制箭头,箭头方向根据曲线控制点到终点的方向计算,更符合有向边的指向。
- 权值标签适配:根据曲线位置调整权值标签的显示位置,避免被曲线遮挡。
- 新增自环处理:针对顶点到自身的边,绘制环形曲线,避免和顶点重叠。
内容的提问来源于stack exchange,提问作者jojo can't code
相关产品推荐
相关产品推荐

