Java實現鼠標繪製多邊形並判斷鼠標打點是否在多邊形內

前言

在GIS(地理信息管理系統)中,判斷一個座標是否在多邊形內部是個經常要遇到的問題。乍聽起來還挺複雜。根據W. Randolph Franklin 提出的PNPoly算法,只需區區幾行代碼就解決了這個問題。PNPoly算法用來判斷一個座標點是否在不規則多邊形內部。

算法詳解

首先我們要知道如何判斷一個點是否在多邊形內部。
從該點任意引一條射線,若點在多邊形內,則與多邊形邊的點應該爲奇數個(因爲不管有幾個交點,最後一個交點必定是穿出多邊形)。而相反,若爲偶數個則點在多邊形外。
在這裏插入圖片描述
PNPlot算法是從該點水平向右引一條射線,根據上面的基本法則進行判斷的。下面是換算式:
在這裏插入圖片描述

代碼

該代碼中涉及到Java消息處理機制,以及內部類的一些知識。

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

public class Main {
	private static int n = 0; // 多邊形頂點個數
	private static int[] x = new int[20]; // 多邊形頂點座標
	private static int[] y = new int[20];
	private static int testx = -1, testy = -1; // 測試點座標
	private static boolean flag = false; // 是否已經構成多邊形

	public static boolean pnplot(int tx, int ty) { // PNplot算法用於判斷點是否在多邊形內
		boolean result = false;
		for (int i = 1, j = n; i <= n; j = i++) {
			if (((y[i] > ty) != (y[j] > ty)) && (tx < (x[j] - x[i]) * (ty - y[i]) * 1.0 / (y[j] - y[i]) + x[i])) {
				result = !result;
			}
		}
		return result;
	}

	public static void main(String[] args) {
		MyFrame mf = new MyFrame();
		MyPanel mp = new MyPanel();
		mf.setContentPane(mp); // 設置窗口的內容面板
		mf.setVisible(true);
		MouseMotionAdapter mma = new MouseMotionAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {
				x[n + 1] = e.getX();
				y[n + 1] = e.getY();
				mp.repaint();
			}
		};
		mp.addMouseListener(new MouseAdapter() { // 添加鼠標監聽器
			@Override
			public void mouseClicked(MouseEvent e) {
				if (!flag) {
					n++;
					x[n] = e.getX();
					y[n] = e.getY();
					x[n + 1] = e.getX();
					y[n + 1] = e.getY();
					
					/*當前點與第一個點的距離在8以內,則形成首尾相連的多邊形*/
					if ((x[n] - x[1]) * (x[n] - x[1]) + (y[n] - y[1]) * (y[n] - y[1]) <= 64 && n > 1) {
						x[n] = x[1];
						y[n] = y[1];
						flag = true;
					}
					mp.repaint(); 	// 重繪
					if (flag) 		// 多邊形已經成功繪製
						mp.removeMouseMotionListener(mma); // 移除MouseMotionListener
				} 
				
				/*若已經繪製好多邊形,則之後的鼠標打點都轉爲測試點*/
				else {
					testx = e.getX();
					testy = e.getY();
					mp.repaint();
				}

			}
		});
		mp.addMouseMotionListener(mma); // 添加鼠標移動監聽器
	}

	@SuppressWarnings("serial")
	static class MyPanel extends JPanel {
		public void paint(Graphics g) {
			g.clearRect(0, 0, this.getWidth(), this.getHeight());	//清除
			for (int i = 1; i <= n; i++) {
				g.drawLine(x[i], y[i], x[i + 1], y[i + 1]);
			}
			if (testx != -1 && testy != -1) {
				Graphics2D g2d = (Graphics2D) g;
				g2d.setStroke(new BasicStroke(3.0f)); // 設置畫筆粗細
				
				/*根據點與多邊形的位置關係設置畫筆顏色*/
				if (pnplot(testx, testy)) {
					g2d.setColor(Color.blue);
				} else
					g2d.setColor(Color.red);
				g2d.drawLine(testx, testy, testx, testy);	//繪製點
			}
		}
	}

	@SuppressWarnings("serial")
	static class MyFrame extends JFrame {
		MyFrame() {
			setSize(500, 500); // 設置框架大小
		}
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章