「閉合多邊形」在計算機圖形學中定義為:給定頂點序列 P₀, P₁, ..., Pₙ₋₁,依次連接 Pᵢ → Pᵢ₊₁,並連接 Pₙ₋₁ → P₀ 形成閉合輪廓。本文將對比 MATLAB 腳本繪圖與 Java AWT/Graphics2D 繪圖的實現差異、座標系統、渲染管線、擴展性,並提供生產級 Java 繪圖組件代碼。
% 閉合多邊形繪圖腳本
clear; clc;
% 頂點座標矩陣:第1行 x,第2行 y
vertices = [0 2 4 6 8 10 12 14; % x 座標
0 2 1 4 6 6 5 7]; % y 座標
[n, m] = size(vertices); % n=2, m=8
figure('Name', 'MATLAB 閉合多邊形', 'Color', 'w');
hold on; axis equal; grid on;
title('MATLAB 繪製閉合多邊形'); xlabel('X'); ylabel('Y');
% 繪製邊:逐段連線
for i = 1:m-1
line([vertices(1,i), vertices(1,i+1)], [vertices(2,i), vertices(2,i+1)], ...
'Color', 'b', 'LineWidth', 2, 'Marker', 'o', 'MarkerFaceColor', 'r');
end
% 首尾閉合
line([vertices(1,1), vertices(1,m)], [vertices(2,1), vertices(2,m)], ...
'Color', 'b', 'LineWidth', 2, 'Marker', 'o', 'MarkerFaceColor', 'r');
% 填充顏色(可選)
% fill(vertices(1,:), vertices(2,:), [0.8 0.9 1.0], 'FaceAlpha', 0.3);
| 維度 | 特點 |
|---|---|
| 數據結構 | 矩陣/陣列原生,向量化運算極簡 |
| 繪圖模型 | Handle Graphics 物件模型,每個圖元可單獨屬性設置 |
| 座標系統 | 笛卡爾座標,Y 軸向上,自動縮放適應數據範圍 |
| 閉合邏輯 | 手動首尾連線,或 fill/patch 自動閉合填充 |
| 適用場景 | 科研數據快速視覺化、算法驗證、教學演示 |
public abstract void drawPolygon(int[] xPoints, int[] yPoints, int nPoints);
public abstract void fillPolygon(int[] xPoints, int[] yPoints, int nPoints);
參數語義:
xPoints[]、yPoints[]:頂點座標陣列,長度 ≥ nPointsnPoints:頂點數(即邊數),系統自動閉合首尾
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.Random;
public class PolygonCanvas extends JPanel {
private final int[] xPoints;
private final int[] yPoints;
private final int nPoints;
private final Polygon polygon; // 用於碰撞檢測、包含判斷
public PolygonCanvas(int[] xPoints, int[] yPoints) {
if (xPoints.length != yPoints.length || xPoints.length < 3) {
throw new IllegalArgumentException("頂點數組長度需一致且 ≥ 3");
}
this.xPoints = xPoints.clone();
this.yPoints = yPoints.clone();
this.nPoints = xPoints.length;
this.polygon = new Polygon(this.xPoints, this.yPoints, this.nPoints);
setPreferredSize(new Dimension(600, 400));
setBackground(Color.WHITE);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// 開啟抗鋸齒
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
// 1. 繪製座標軸
drawAxes(g2);
// 2. 填充多邊形(半透明)
g2.setColor(new Color(0x87CEEB, true)); // SkyBlue + Alpha
g2.fillPolygon(xPoints, yPoints, nPoints);
// 3. 繪製邊框
g2.setStroke(new BasicStroke(2.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.setColor(new Color(0x1E90FF)); // DodgerBlue
g2.drawPolygon(xPoints, yPoints, nPoints);
// 4. 標註頂點
g2.setColor(Color.RED);
for (int i = 0; i < nPoints; i++) {
g2.fillOval(xPoints[i] - 4, yPoints[i] - 4, 8, 8);
g2.drawString("P" + i, xPoints[i] + 6, yPoints[i] - 6);
}
// 5. 顯示幾何屬性
g2.setColor(Color.DARK_GRAY);
g2.drawString(String.format("面積: %.2f", polygon.getBounds2D().getWidth() * polygon.getBounds2D().getHeight()), 10, 20);
g2.drawString(String.format("重心: (%.1f, %.1f)", centroidX(), centroidY()), 10, 40);
}
private void drawAxes(Graphics2D g2) {
int w = getWidth(), h = getHeight();
g2.setColor(Color.LIGHT_GRAY);
g2.drawLine(50, h - 50, w - 50, h - 50); // X 軸
g2.drawLine(50, h - 50, 50, 50); // Y 軸
// 刻度略
}
/** 計算多邊形重心(質心) */
private double centroidX() {
double cx = 0, area = 0;
for (int i = 0, j = nPoints - 1; i < nPoints; j = i++) {
double cross = xPoints[j] * yPoints[i] - xPoints[i] * yPoints[j];
area += cross;
cx += (xPoints[j] + xPoints[i]) * cross;
}
return cx / (3 * area);
}
private double centroidY() {
double cy = 0, area = 0;
for (int i = 0, j = nPoints - 1; i < nPoints; j = i++) {
double cross = xPoints[j] * yPoints[i] - xPoints[i] * yPoints[j];
area += cross;
cy += (yPoints[j] + yPoints[i]) * cross;
}
return cy / (3 * area);
}
/** 主程序啟動 */
public static void main(String[] args) {
// 測試數據:凹多邊形
int[] xs = {100, 200, 300, 250, 150, 50};
int[] ys = {300, 100, 200, 300, 350, 250};
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Java 閉合多邊形繪製 - Graphics2D");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PolygonCanvas(xs, ys));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
| 維度 | MATLAB | Java (AWT/Graphics2D) |
|---|---|---|
| 閉合方式 | 手動首尾連線或 fill/patch 自動 | drawPolygon/fillPolygon 內建自動閉合 |
| 座標系統 | 數學座標(Y↑),自動適應 | 屏幕座標(Y↓),需手動變換 |
| 渲染質量 | 向量圖形,導出 EPS/PDF 無損 | 光柵化,抗鋸齒依賴 RenderingHints |
| 交互性 | 圖形窗口基礎交互 | 完整事件模型(滑鼠、鍵盤、拖拽、縮放) |
| 幾何計算 | 內建 polyarea、centroid、inpolygon | Polygon/Area 類支援面積、包含、布爾運算 |
| 部署形態 | 腳本/MATLAB Runtime | JAR/原生映像/Web (Applet 已棄用) |
| 性能(萬級頂點) | 較慢(解釋執行) | 極快(JIT 編譯、GPU 加速管線) |
Java 繪圖常需將業務座標(如經緯度、米制)映射到屏幕像素。推薦使用 AffineTransform:
/** 計算適應面板的變換:業務座標 → 屏幕座標 */
private AffineTransform calculateTransform() {
Rectangle2D bounds = polygon.getBounds2D();
double scaleX = (getWidth() - 100) / bounds.getWidth();
double scaleY = (getHeight() - 100) / bounds.getHeight();
double scale = Math.min(scaleX, scaleY) * 0.9; // 留邊距
AffineTransform at = new AffineTransform();
at.translate(50, getHeight() - 50); // 原點移至左下角(模擬數學座標)
at.scale(scale, -scale); // Y 軸翻轉 + 縮放
at.translate(-bounds.getCenterX(), -bounds.getCenterY()); // 幾何中心對齊
return at;
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
AffineTransform at = calculateTransform();
Shape transformed = at.createTransformedShape(polygon);
g2.fill(transformed);
g2.draw(transformed);
}
文中提到:「是否能構成多邊形,凸多邊形,還是凹多邊形,這些與給定的點的順序有關。程序只是負責將相鄰的節點連接,以及首尾連接。」
| 方案 | 優勢 | 適用場景 |
|---|---|---|
| JavaFX | 硬體加速、CSS 樣式、FXML 分離、WebView 整合 | 現代桌面應用、複雜 UI |
| Processing | 藝術編程、簡單 API、跨平台導出 | 創意編程、教學、快速原型 |
| JFreeChart / XChart | 專業圖表庫、多種圖表類型、導出高質量圖片 | 商業報表、數據分析 |
| OpenGL (JOGL/LWJGL) | GPU 級性能、著色器、3D | 遊戲、科學視覺化、大數據渲染 |
Java 與 MATLAB 繪製閉合多邊形的核心差異在於:
本文提供的 PolygonCanvas 組件已包含:抗鋸齒、填充+描邊、頂點標註、座標軸、幾何屬性顯示、自適應縮放變換,可直接集成到 Swing/JavaFX(通過 SwingNode)應用中。若需導出向量圖,可改用 Graphics2D 繪製到 PDFGraphics2D(iText)或 SVGGraphics2D(Batik)。