射线法:判断点是否在区域内(思路+java实现)

判断一个点是否在一个区域中时可以使用射线法,一下将介绍射线法的思路以及实现过程:

一、思路

射线法就是做一条从该点出发的射线,当穿过区域边界的次数为偶数时该点在区域外,如果是奇数则在区域内:
在这里插入图片描述原理:

一条射线穿过区域的边界时只有两种情况:一种是穿入一种是穿出。对于射线来说,只要一次穿入必定对应一次穿出。

1、所以当一个点在区域外时,作出的射线经过区域边界时一定是穿入,一次穿入对应一次穿出,所以最终的穿越次数一定是偶数;

2、当该点在区域内时,第一次穿越边界一定是穿出,下一次如果穿入就又会对应一次穿出,所以区域内的点会多出单独的一次穿出,所以最终的穿越总数是奇数;

二、实现

既然知道了原理和思路,那我们进行具体实现;

为了简单处理,模拟的射线是向左射出的水平射线,并使用了java中的Point2D类,该类用于存储点的横纵坐标,方便处理;
在实现前需要知道几种特殊情况:

1、点在边界上,在边界上应该属于区域内,所以我们只需要增加相应的逻辑判断,只要点在边界上就属于区域内;

2、当射线穿过交点时:
在这里插入图片描述这种情况应该算几次穿越呢,我们可以这样设想。因为我们模拟水平射线,如果射线与一条线段有交点,那么这条线段的两个端点一定在射线的两侧,所以可以将穿过的交点统一看做是射线的上侧,如果两个端点都在射线的一侧那就不算穿越,如果两个端点都在射线的两侧就算一次穿越,所以:
在这里插入图片描述P1,P2都在射线一侧,不算穿越,P1,P3在射线两侧,算一次穿越,所以该点在区域内;

3、射线穿过边界线:
在这里插入图片描述这种情况可以当作第一种情况处理,因为按照第一种情况P1,P2都在射线上侧,在射线的一侧,所以不算穿越;

java代码

/**
     * 判断该点是否在区域内
     * 说明:该点出发的射线模拟为向左射出的水平射线
     *
     * @param pointList
     * @param point
     * @return
     */
    public static Boolean isInArea(List<Point2D.Double> pointList, Point2D.Double point) {
        Boolean flag = true; //是否在区域内标识
        Point2D.Double p, p1, p2;
        Integer across = 0; //穿越次数
        double precision = 2e-10; //浮点类型计算时候的比较容差
        p = point;

        for (int i = 0; i < pointList.size(); i++) {
            p1 = pointList.get(i);
            int j = (i + 1) >= pointList.size() ? 0 : (i + 1);
            p2 = pointList.get(j);
            // 1、判断是否在点上
            if (p1.equals(p) || p2.equals(p)) {
                return flag;
            }
            // 2、该点是否在两个端点之间
            if (p.y <= Math.max(p1.y, p2.y) && p.y >= Math.min(p1.y, p2.y)) {
                // 3、判断该点是否在两点连成的线段上
                if (p.x <= Math.max(p1.x, p2.x) && p.x >= Math.min(p1.x, p2.x)) {
                    // 其一:该线段是水平线
                    if (p1.y == p2.y) {
                        if (p.y == p1.y) {
                            return flag;
                        }
                    }
                    // 其二:该线段是垂直线
                    if (p1.x == p2.x) {
                        if (p.x == p1.x) {
                            return flag;
                        }
                    }
                    // 其三:该线段是斜线
                    double xianShangY = p1.y + (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x);
                    if (Math.abs(xianShangY - p.y) < precision) {
                        // 在线上
                        return flag;
                    }
                }
                // 4、判断两个端点是否越过该点(因为是左水平射线)
                if (p.x <= Math.min(p1.x, p2.x)) {
                    continue;
                }
               /*
                    该点位于两个端点之间,判断是否穿过顶点(穿过顶点时,顶点按照在射线上部处理)
                    穿过顶点时,只有两个端点在射线两侧的才按照穿越处理
                */
                if (p1.y != p2.y && p.y != Math.min(p1.y, p2.y)) {
                    across++;
                }
            }
        }
        if (across % 2 == 0) {
            // 偶数,在外部
            return !flag;
        }
        return flag;
    }
  • 13
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
射线判断是否在多边形内部的思路如下: 1. 将多边形边界上的所有与待判断的连线,分别与x轴正向的射线相交。 2. 统计相交的数量,如果是奇数,在多边形内部;如果是偶数,在多边形外部。 具体实现步骤如下: 1. 构造一个从待判断发出的射线,方向为x轴正向。 2. 遍历多边形的边界,判断每条边是否射线相交。 2.1 如果射线穿过边界,记录交的数量。 3. 如果交数量为奇数,在多边形内部,返回true;否则,在多边形外部,返回false。 以下是C++代码实现: ```c++ #include <iostream> using namespace std; struct Point{ double x, y; }; struct Segment{ Point a, b; }; // 计算叉积 double cross_product(Point A, Point B, Point C){ return (B.x - A.x) * (C.y - B.y) - (C.x - B.x) * (B.y - A.y); } // 判断射线是否和线段相交 bool is_intersect(Segment seg, Point p){ if (seg.a.y == seg.b.y) return false; // 排除水平的线段 if (p.y < min(seg.a.y, seg.b.y)) return false; // 在线段下方 if (p.y >= max(seg.a.y, seg.b.y)) return false; // 在线段上方 double x = (p.y - seg.a.y) * (seg.b.x - seg.a.x) / (seg.b.y - seg.a.y) + seg.a.x; // 计算交的x坐标 return x > p.x; // 判断是否射线右侧 } // 判断是否在多边形内部 bool is_inside_polygon(Point p, Point* polygon, int n){ int count = 0; for (int i = 0; i < n; i++){ if (is_intersect({polygon[i], polygon[(i+1)%n]}, p)) count++; } return count % 2 == 1; } int main(){ Point p = {1, 1}; Point polygon[] = {{0,0}, {2,0}, {2,2}, {0,2}}; int n = 4; if (is_inside_polygon(p, polygon, n)){ cout << "Point is inside polygon" << endl; } else { cout << "Point is outside polygon" << endl; } return 0; } ``` 其中,cross_product函数用于计算叉积,is_intersect函数用于判断射线是否和线段相交,is_inside_polygon函数用于判断是否在多边形内部。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值