问题介绍
给定一个点和一个多边形(由点集的点依次连接构成),需要判断该点是否在多边形的内部。
方法简述
要判断一个点是否在多边形内部,只需要从点出发,水平向右做一条射线,然后计算射线与多边形的交点数量。若交点数量为偶数,则点在多边形外部;若交点数量为奇数,则点在多边形内部。
计算交点数量
计算交点的方法主要有以下三种:
- 射线直接与某一条边相交(非边的端点)
- 射线与两条边的交点相交
- 射线与一条边有重合片段(边的斜率为0,且y轴坐标与射线的y轴坐标相等)
要判断出上面的左拐、右拐情况,需要借助向量叉乘
![[图片]](https://img-blog.csdnimg.cn/b9cd621f0b9c48cc9a57ef87056bda4e.png#pic_center)
一般多边形可以由一个点集合表示,点集合中的各个点按照顺序相连即可形成多边形

![[图片]](https://img-blog.csdnimg.cn/b4ab8a7195b1427080c23dc17feca4bb.png#pic_center)
遍历过程
for (int i = 0; i < pointList.size(); i++) {
//多边形线1(line2):pointI-pointIPlus1; //多边形线2(line3):pointIPlus1-pointIPlus2; //多边形线3(line4):pointIPlus2-pointIPlus3 Point pointI = pointList.get(i); Point pointIPlus1 = pointList.get((i + 1) % pointList.size()); Point pointIPlus2 = pointList.get((i + 2) % pointList.size()); Point pointIPlus3 = pointList.get((i + 3) % pointList.size()); if(射线与line2相交){
直接把相交点数+1 }eles if(pointIPlus1在射线上面){
if(pointIPlus2不在射线上面){
判断是否满足图1的第二种情况,满足则交相交点+1 }else{
判断是否满足图1的第三种情况,满足则交相交点+1 } } }
讯享网
注意点
![[图片]](https://img-blog.csdnimg.cn/202ae53e7a5c46b48e11f48f054d5423.png#pic_center)
代码实现
【Point】
讯享网package com.ruoyi.algorithm.common.entity; import lombok.Data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @Data public class Point implements Serializable, Cloneable {
private static final long serialVersionUID = 1L; private String id; / * 这个属性没啥用,我主要是用y、z,懒得修改了 */ private double x; private double y; private double z; public Point() {
} public Point(double x, double y, double z) {
this.x = x; this.y = y; this.z = z; } @Override public Point clone() throws CloneNotSupportedException {
return (Point) super.clone(); } public static ArrayList<Point> cloneList(List<Point> pointList) throws CloneNotSupportedException {
ArrayList<Point> clone = new ArrayList<>(); for (Point point : pointList) {
clone.add(point.clone()); } return clone; } }
【PointUtil 】
package com.ruoyi.algorithm.get_maximumArea_rectangles_in_a_simple_polygon.version.v1; import com.ruoyi.algorithm.common.entity.Point; import java.util.List; public class PointUtil {
/ * 判断两个点是否相等 */ public static boolean judgeWhetherTwoPointsIsSame(Point p1, Point p2) {
if (p1 == null || p2 == null) {
return false; } if (MathUtil.getDistanceOfTwoPoint(p1.getY(), p1.getZ(), p2.getY(), p2.getZ()) < 0.0001) {
return true; } else {
return false; } } }
【MathUtil】
讯享网package com.ruoyi.algorithm.get_maximumArea_rectangles_in_a_simple_polygon.version.v1; / * 数学工具 */ public class MathUtil {
public static void main(String[] args) {
System.out.println(MathUtil.calculateAngleOfVector(-1, -1)); } / * 获取一条直线的斜率 * * @param lx1 * @param ly1 * @param lx2 * @param ly2 * @return */ public static double getKOfLine(double lx1, double ly1, double lx2, double ly2) {
double precisionError = 0.000000001; double k; if (Math.abs(lx2 - lx1) < precisionError) {
//--if--线段是垂直线 k = Double.MAX_VALUE; } else if (Math.abs(ly2 - ly1) < precisionError) {
//--if--线段是水平线 k = 0; } else {
//--if--线段是倾斜的线 k = (ly2 - ly1) * 1.0 / (lx2 - lx1); } return k; } / * 获取直线截距 * * @param x 直线上的点 * @param y 直线上的点 * @param k * @return */ public static double getBOfLine(double x, double y, double k) {
double b; if (Math.abs(k - Double.MAX_VALUE) < 0.01) {
b = Double.MAX_VALUE; } else if (k == 0) {
b = y; } else {
b = y - k * x; } return b; } / * 计算点到直线的距离 * * @param x * @param y * @param lx1 * @param ly1 * @param lx2 * @param ly2 * @return */ public static double calculateDistanceOfPointAndLine(double x, double y, double lx1, double ly1, double lx2, double ly2) {
double k = MathUtil.getKOfLine(lx1, ly1, lx2, ly2); if (k == 0) {
return Math.abs(y - ly1); } else if (k == Double.MAX_VALUE) {
return Math.abs(x - lx1); } else {
double b = ly2 - k * lx2; return Math.abs(k * x - y + b) / Math.sqrt(1 + k * k); } } / * 获取两个点之间的距离 * * @return */ public static double getDistanceOfTwoPoint(double x1, double y1, double x2, double y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); } / * 计算向量的模 * * @param x * @param y * @return */ public static double calculateModulusOfVector(double x, double y) {
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); } / * 求(x,y)的角度(0,360),从x坐标轴正方向开始计算 * * @param x2 * @param y2 * @return */ public static double calculateAngleOfVector(double x2, double y2) {
double x1 = 1; double y1 = 0; double radian = Math.acos((x1 * x2 + y1 * y2) / (MathUtil.calculateModulusOfVector(x1, y1) * MathUtil.calculateModulusOfVector(x2, y2))); double angle = Math.toDegrees(radian); return y2 > 0 ? angle : 360 - angle; } }
【PolygonUtil】
package com.ruoyi.algorithm.get_maximumArea_rectangles_in_a_simple_polygon.version.v1; import com.ruoyi.algorithm.common.entity.Point; import java.util.List; / * @Author dam * @create 2023/9/11 17:22 */ public class PolygonUtil {
/ * 吉林大学的ACM方法:判断点是否处于多边形内部(点处于多边形的边上同样被视为处于多边形内部) * * @param pointList * @param pointA * @return */ public static boolean judgeWhetherThePointInPolygon(List<Point> pointList, Point pointA) throws Exception {
///首先给点集构造一个包络矩形,判断点是否在包络矩形外部 double minY = Double.MAX_VALUE; double maxY = -Double.MAX_VALUE; double minZ = Double.MAX_VALUE; double maxZ = -Double.MAX_VALUE; for (Point point : pointList) {
minY = Math.min(minY, point.getY()); maxY = Math.max(maxY, point.getY()); minZ = Math.min(minZ, point.getZ()); maxZ = Math.max(maxZ, point.getZ()); } if (pointA.getY() < minY || pointA.getY() > maxY || pointA.getZ() < minZ || pointA.getZ() > maxZ) {
return false; } ///判断点是否在多边形的内部,不包含在多边形的某一条边上 //构建线1:点A向右延伸的水平射线 Point line1_start = pointA; Point line1_end = pointA.clone(); line1_end.setY(Double.MAX_VALUE); int num = getIntersectNumOfLineSegmentWithPolygon(pointList, line1_start, line1_end); boolean isContain = num % 2 != 0; ///如果上面识别出来说点不在多边形内部,尝试看看点在不在某一条边上,如果在,则点在多边形内 if (isContain == false) {
for (int i = 0; i < pointList.size(); i++) {
int j = (i + 1) % pointList.size(); if (PolygonUtil.judgeWhetherThePointInTheLine(pointList.get(i), pointList.get(j), pointA) == true || PointUtil.judgeWhetherTwoPointsIsSame(pointList.get(i), pointA) || PointUtil.judgeWhetherTwoPointsIsSame(pointList.get(j), pointA)) {
isContain = true; break; } } } return isContain; } / * 判断点 p 是否在线段 line 上 * * @param line_start * @param line_end * @param p * @return */ private static boolean judgeWhetherThePointInTheLine(Point line_start, Point line_end, Point p) {
double xmulti = xmulti(line_end, p, line_start); return ((Math.abs(xmulti - 0) < 0.1 || PolygonUtil.calculateDistanceOfPointAndLine(line_start, line_end, p) < 0.000001) && (((p.getY() - line_start.getY()) * (p.getY() - line_end.getY()) < 0) || ((p.getZ() - line_start.getZ()) * (p.getZ() - line_end.getZ()) < 0))); } / * 计算点到直线的距离 * * @param line_start * @param line_end * @param p * @return */ private static double calculateDistanceOfPointAndLine(Point line_start, Point line_end, Point p) {
return MathUtil.calculateDistanceOfPointAndLine(p.getY(), p.getZ(), line_start.getY(), line_start.getZ(), line_end.getY(), line_end.getZ()); } / * 计算线段和多边形的交点 * * @param pointList * @param line1_start * @param line1_end * @return * @throws Exception */ private static int getIntersectNumOfLineSegmentWithPolygon(List<Point> pointList, Point line1_start, Point line1_end) throws Exception {
//统计射线所穿过多边形中点的数量 int num = 0; //遍历多边形的每条线 for (int i = 0; i < pointList.size(); i++) {
//多边形线1(line2):pointI-pointIPlus1; 多边形线2(line3):pointIPlus1-pointIPlus2; 多边形线3(line4):pointIPlus2-pointIPlus3 Point pointI = pointList.get(i); Point pointIPlus1 = pointList.get((i + 1) % pointList.size()); Point pointIPlus2 = pointList.get((i + 2) % pointList.size()); Point pointIPlus3 = pointList.get((i + 3) % pointList.size()); //射线line1和多边形上面的线line2是否相交 boolean whetherLine1AndLine2IsIntersect = judgeWhetherTwoLineSegmentIsIntersect(line1_start, line1_end, pointI, pointIPlus1); //点pointIPlus1是否在射线上 boolean whetherPointIPlus1InRadial = judgeWhetherThePointInTheLine(line1_start, line1_end, pointIPlus1); //点pointIPlus2是否在射线上 boolean whetherPointIPlus2InRadial = judgeWhetherThePointInTheLine(line1_start, line1_end, pointIPlus2); if (whetherLine1AndLine2IsIntersect == true) {
num++; int temp = 0; } else if (whetherPointIPlus1InRadial && (!whetherPointIPlus2InRadial && (xmulti(pointI, pointIPlus1, line1_start) * xmulti(pointIPlus1, pointIPlus2, line1_start) > 0) || (whetherPointIPlus2InRadial && (xmulti(pointI, pointIPlus2, line1_start) * xmulti(pointIPlus2, pointIPlus3, line1_start) > 0)))) {
num++; int temp = 0; } } return num; } / * 判断两线段是否相交,当两线段相交且交点不是其中一线段的端点时返回true * * @param line1_start * @param line1_end * @param line2_start * @param line2_end * @return */ public static boolean judgeWhetherTwoLineSegmentIsIntersect(Point line1_start, Point line1_end, Point line2_start, Point line2_end) throws Exception {
double line1_minY = Math.min(line1_start.getY(), line1_end.getY()); double line1_maxY = Math.max(line1_start.getY(), line1_end.getY()); double line1_minZ = Math.min(line1_start.getZ(), line1_end.getZ()); double line1_maxZ = Math.max(line1_start.getZ(), line1_end.getZ()); double line2_minY = Math.min(line2_start.getY(), line2_end.getY()); double line2_maxY = Math.max(line2_start.getY(), line2_end.getY()); double line2_minZ = Math.min(line2_start.getZ(), line2_end.getZ()); double line2_maxZ = Math.max(line2_start.getZ(), line2_end.getZ()); if ((line1_minY > line2_maxY || line2_minY > line1_maxY || line1_minZ > line2_maxZ || line2_minZ > line1_maxZ)) {
return false; } else {
boolean isTwoLineIntersect = lsinterls(line1_start, line1_end, line2_start, line2_end); if (isTwoLineIntersect == false) {
return false; } //求交点,用来判断该交点是否为端点 Point intersectPoint = PolygonUtil.getIntersectPointOfTwoStraightLine(line1_start, line1_end, line2_start, line2_end, -1); return ((isTwoLineIntersect) && (!PointUtil.judgeWhetherTwoPointsIsSame(intersectPoint, line1_start)) && (!PointUtil.judgeWhetherTwoPointsIsSame(intersectPoint, line1_end)) && (!PointUtil.judgeWhetherTwoPointsIsSame(intersectPoint, line2_start)) && (!PointUtil.judgeWhetherTwoPointsIsSame(intersectPoint, line2_end))); } } / * 求两条直线的交点 * * @param line1_start * @param line1_end * @param line2_start * @param line2_end * @param polygonIndex * @return * @throws Exception */ public static Point getIntersectPointOfTwoStraightLine(Point line1_start, Point line1_end, Point line2_start, Point line2_end, int polygonIndex) throws Exception {
//获取两线段的k和b double k1 = PolygonUtil.getKOfLine(line1_start, line1_end); double b1 = PolygonUtil.getBOfLine(line1_start, k1); double k2 = PolygonUtil.getKOfLine(line2_start, line2_end); double b2 = PolygonUtil.getBOfLine(line2_start, k2); if (k1 == 0 && k2 == 0) {
//斜率相同也有可能相交 if (PointUtil.judgeWhetherTwoPointsIsSame(line1_start, line2_start) || PointUtil.judgeWhetherTwoPointsIsSame(line1_start, line2_end)) {
return line1_start; } else if (PointUtil.judgeWhetherTwoPointsIsSame(line1_end, line2_start) || PointUtil.judgeWhetherTwoPointsIsSame(line1_end, line2_end)) {
return line1_end; } else {
return null; } } //获取两线段的交点 double y = 0; double z = 0; double offSet = 0.000000001; if ((Math.abs(k1 - 0) <= offSet) || (Math.abs(k2 - 0) <= offSet) || (Math.abs(k1 - Double.MAX_VALUE) <= offSet) || (Math.abs(k2 - Double.MAX_VALUE) <= offSet)) {
if (Math.abs(k1 - 0) <= offSet) {
//line1为水平线时,line2必不可能为水平线,因为已经证明两线相交,平行线必不相交 z = line1_start.getZ(); y = (z - b2) / k2; if (Math.abs(k2 - Double.MAX_VALUE) < offSet) {
y = line2_start.getY(); } else {
y = (z - b2) / k2; } } else if (Math.abs(k2 - 0) <= offSet) {
z = line2_start.getZ(); y = (z - b1) / k1; if (Math.abs(k1 - Double.MAX_VALUE) < offSet) {
y = line1_start.getY(); } else {
y = (z - b1) / k1; } } else if (Math.abs(k1 - Double.MAX_VALUE) <= offSet) {
y = line1_start.getY(); z = k2 * y + b2; } else if (Math.abs(k2 - Double.MAX_VALUE) <= offSet) {
y = line2_start.getY(); z = k1 * y + b1; } else {
throw new Exception("判断情况考虑不全面,请仔细考虑"); } } else {
y = (b2 - b1) / (k1 - k2); z = (k2 * b1 - k1 * b2) / (k2 - k1); } if (Math.abs(z - 290.45) < 1) {
int temp = 0; } if (Double.isInfinite(y)) {
throw new Exception("y为无穷"); } Point point = new Point(0, y, z); return point; } / * 求一条直线的截距 * * @param p 线的一个点 * @param k 线的斜率 * @return */ public static double getBOfLine(Point p, double k) {
return MathUtil.getBOfLine(p.getY(), p.getZ(), k); } / * 确定两条线段是否相交,端点相交也是相交 * * @param line1_start * @param line1_end * @param line2_start * @param line2_end * @return */ public static boolean lsinterls(Point line1_start, Point line1_end, Point line2_start, Point line2_end) {
double k1 = PolygonUtil.getKOfLine(line1_start, line1_end); double k2 = PolygonUtil.getKOfLine(line2_start, line2_end); if (Math.abs(k1 - k2) < 0.000001) {
return false; } return ((Math.max(line1_start.getY(), line1_end.getY()) >= Math.min(line2_start.getY(), line2_end.getY())) && (Math.max(line2_start.getY(), line2_end.getY()) >= Math.min(line1_start.getY(), line1_end.getY())) && (Math.max(line1_start.getZ(), line1_end.getZ()) >= Math.min(line2_start.getZ(), line2_end.getZ())) && (Math.max(line2_start.getZ(), line2_end.getZ()) >= Math.min(line1_start.getZ(), line1_end.getZ())) && (xmulti(line2_start, line1_end, line1_start) * xmulti(line1_end, line2_end, line1_start) >= 0) && (xmulti(line1_start, line2_end, line2_start) * xmulti(line2_end, line1_end, line2_start) >= 0)); } / * (P1-P0)*(P2-P0)的叉积 * 若结果为正,则<P0,P1>在<P0,P2>的顺时针方向; * 若结果为零,则<P0,P1><P0,P2>共线; * 若结果为负,则<P0,P1>在<P0,P2>的在逆时针方向; * <p> * 可以根据这个函数确定两条线段在交点处的转向, * 比如确定p0p1和p1p2在p1处是左转还是右转,只要求 * (p2-p0)*(p1-p0),若<0则左转,>0则右转,=0则共线 * * @param p1 * @param p2 * @param p0 * @return <0:p2在p1处左拐 =0:直走 >0:p2在p1处右拐 */ private static double xmulti(Point p1, Point p2, Point p0) {
return ( (p1.getY() - p0.getY()) * (p2.getZ() - p0.getZ()) - (p2.getY() - p0.getY()) * (p1.getZ() - p0.getZ()) ); } / * 求一条直线的斜率 * * @param p1 * @param p2 * @return */ public static double getKOfLine(Point p1, Point p2) {
return MathUtil.getKOfLine(p1.getY(), p1.getZ(), p2.getY(), p2.getZ()); } }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/27106.html