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); // 设置框架大小
		}
	}
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值