Qt判断一个点在多边形内还是外(支持凸边形和凹变形)

Qt判断一个点在多边形内还是外(支持凸边形和凹变形)_qt 判断点是否在多边形内-CSDN博客

这里实现的方法是转载于https://blog.csdn.net/trj14/article/details/43190653https://blog.csdn.net/WilliamSun0122/article/details/77994526
来实现的,并且按照Qt的规则进行了调整。
以下实现方法有四种,每种方法的具体讲解在转载的博客中有说明,这里不做重复阐述。
这里只说下代码的具体实现和每种方法的时间复杂度。

强烈推荐第一种方法,其它三种针对一些特殊图形或多或少都有一些问题。

方法一:射线法

时间复杂度:O(n) 。
适用范围:任意多边形。
优点:不需考虑精度误差和多边形点给出的顺序。
算法思想:以被测点Q为端点,向任意方向作射线(一般水平向右作射线),统计该射线与多边形的交点数。如果为奇数,Q在多边形内;如果为偶数,Q在多边形外。计数的时候会有一些特殊情况,如图

//判断点P在多边形内-射线法
bool Widget::InsidePolygon( QVector<QPointF> polygon,QPointF pt )
{
    int i,j;
    bool inside,redo;

    int N = polygon.size();
    redo = true;
    for (i = 0;i < N;++i)
    {
        // 是否在顶点上
        if (polygon[i].x() == pt.x() && polygon[i].y() == pt.y())
        {
            redo = false;
            inside = true;
            break;
        }
    }

    while (redo)
    {
        redo = false;
        inside = false;
        for (i = 0,j = N - 1;i < N;j = i++)
        {
            if ( (polygon[i].y() < pt.y() && pt.y() < polygon[j].y()) ||
                (polygon[j].y() < pt.y() && pt.y() < polygon[i].y()) )
            {
                if (pt.x() <= polygon[i].x() || pt.x() <= polygon[j].x())
                {
                    double _x = (pt.y()-polygon[i].y())*(polygon[j].x()-polygon[i].x())/(polygon[j].y()-polygon[i].y())+polygon[i].x();
                    if (pt.x() < _x)          // 在线的左侧
                        inside = !inside;
                    else if (pt.x() == _x)    // 在线上
                    {
                        inside = true;
                        break;
                    }
                }
            }
            else if ( pt.y() == polygon[i].y())
            {
                if (pt.x() < polygon[i].x())    // 交点在顶点上
                {
                    if (polygon[i].y() > polygon[j].y())
                        pt.setY(pt.y() - 1);
                    else
                        pt.setY(pt.y() + 1);
                    redo = true;
                    break;
                }
            }
            else if ( polygon[i].y() ==  polygon[j].y() && pt.y() == polygon[i].y() &&
                ((polygon[i].x() < pt.x() && pt.x() < polygon[j].x()) ||
                (polygon[j].x() < pt.x() && pt.x() < polygon[i].x())) )// 在水平的边界线上
            {
                inside = true;
                break;
            }
        }
    }

    return inside;
}

方法二:面积和判别法

时间复杂度:大于O(n)。
适用范围:所有凸边形,部分凹变形。
优点:算法简单。
缺点:有精度要求,强调多边形点给出的方向(逆时针)。
算法思想:如果点在多边形内部或者边上,那么点与多边形所有边组成的三角形面积和等于多边形面积。多边形的面积可以用叉积计算即连接坐标原点和各顶点形成向量,所有向量叉积的0.5的和即为多边形面积。不过计算面积是会有一定误差的,需要设置精度的误差范围。

//面积和判别法
bool InsidePolygon3( QVector<QPointF> polygon,QPointF pt )
{
    int i,j;
    bool inside = false;
    double polygon_area = 0;
    double trigon_area = 0;
    int N = polygon.size();

    for (i = 0,j = N - 1;i < N;j = i++)
    {
        polygon_area += polygon[i].x() * polygon[j].y() - polygon[j].x() * polygon[i].y();
        trigon_area += abs(
            pt.x() * polygon[i].y() -
            pt.x() * polygon[j].y() -
            polygon[i].x() * pt.y() +
            polygon[i].x() * polygon[j].y() +
            polygon[j].x() * pt.y() -
            polygon[j].x() * polygon[i].y()
            );
    }

    trigon_area *= 0.5;
    polygon_area = abs(polygon_area * 0.5);
    if ( fabs(trigon_area - polygon_area) < 1e-7 )
        inside = true;

    return inside;
}

方法三:点线判别法

时间复杂度:O(n)。
适用范围:所有凸边形,部分凹变形。
算法思想:对于多边形(正向,即逆时针),如果一个点它的所有有向边的左边,那么这个点一定在多边形内部。利用叉积正好可以判断点与给定边的关系,即点是在边的左边右边还是边上。

//点线判别法
bool InsidePolygon4( QVector<QPointF> polygon,QPointF p )
{
    int i,j;
    bool inside = false;
    int count1 = 0;
    int count2 = 0;
    int N = polygon.size();

    for (i = 0,j = N - 1;i < N;j = i++)
    {
        double value = (p.x() - polygon[j].x()) * (polygon[i].y() - polygon[j].y()) - (p.y() - polygon[j].y()) * (polygon[i].x() - polygon[j].x());
        if (value > 0)
            ++count1;
        else if (value < 0)
            ++count2;
    }

    if (0 == count1 ||
        0 == count2)
    {
        inside = true;
    }
    return inside;
}

方法四:角度和判别法

时间复杂度:O(n)。
适用范围:所有凸边形,部分凹变形。
优点:不强调多边形点给出顺序。
缺点:这个算法对精度的要求很高(会造成很大精度误差)。
算法思想:连接被测点与多边形所有顶点所形成的所有角的角度和在精度范围内等于则该点在多边形内,否则在多边形外。

// 根据需要不判断顶点
bool IsPointInLine( QPointF &pt,QPointF &pt1,QPointF &pt2 )
{
    bool inside = false;
    if (pt.y() == pt1.y() &&
        pt1.y() == pt2.y() &&
        ((pt1.x() < pt.x() && pt.x() < pt2.x()) ||
        (pt2.x() < pt.x() && pt.x() < pt1.x())) )
    {
        inside = true;
    }
    else if (pt.x() == pt1.x() &&
        pt1.x() == pt2.x() &&
        ((pt1.y() < pt.y() && pt.y() < pt2.y()) ||
        (pt2.y() < pt.y() && pt.y() < pt1.y())) )
    {
        inside = true;
    }
    else if ( ((pt1.y() < pt.y() && pt.y() < pt2.y()) ||
        (pt2.y() < pt.y() && pt.y() < pt1.y())) &&
        ((pt1.x() < pt.x() && pt.x() < pt2.x()) ||
        (pt2.x() < pt.x() && pt.x() < pt1.x())) )
    {
        if (0 == (pt.y()-pt1.y())/(pt2.y()-pt1.y())-(pt.x() - pt1.x()) / (pt2.x()-pt1.x()))
        {
            inside = true;
        }
    }
    return inside;
}

//角度和判别法
bool InsidePolygon2( QVector<QPointF> polygon,QPointF p)
{
    int i,j;
    double angle = 0;
    bool inside = false;
    int N = polygon.size();

    for (i = 0,j = N - 1;i < N;j = i++)
    {
        if (polygon[i].x() == p.x() &&    // 是否在顶点上
            polygon[i].y() == p.y())
        {
            inside = true;
            break;
        }
        else if (IsPointInLine(p,polygon[i],polygon[j]))    // 是否在边界线上
        {
            inside = true;
            break;
        }

        double x1,y1,x2,y2;
        x1 = polygon[i].x() - p.x();
        y1 = polygon[i].y() - p.y();
        x2 = polygon[j].x() - p.x();
        y2 = polygon[j].y() - p.y();

        double radian = atan2(y1,x1) - atan2(y2,x2);
        radian = abs(radian);
        if (radian > M_PI) radian = 2* M_PI - radian;
        angle += radian;            // 计算角度和
    }

    if ( fabs(6.28318530717958647692 - angle) < 1e-7 )
        inside = true;

    return inside;
}

  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PCL凹包算法可以用于提取云数据的边界。该算法的实现步骤如下: 1. 首先,需要找到一个初始边界A,它是最围的,并将其存入边界集合。 2. 然后,根据向量AB的方向,寻找下一个点B,使得向量AB朝判断方向的方法是计算最左和最右两个,并计算过这两个的直线方程。根据直线方程判断方向,选取B。 3. 接下来,在A上逆时针方向旋转向量AB,寻找X,使得向量AB与向量AX的夹角最小。 4. 重复步骤2和步骤3,直到找到所有的边界。 具体的代码实现可以参考引用\[1\]中的示例代码。该代码使用了PCL库中的ConcaveHull类来实现凹包算法。通过设置圆的半径大小,可以控制边界的提取程度。最后,将提取到的边界存入一个新的云数据中。 希望以上信息对您有所帮助! #### 引用[.reference_title] - *1* [PCL 云边界提取/边界轮廓提取(附完整c++代码)](https://blog.csdn.net/weixin_43896283/article/details/129467746)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [PCL 平面云的凹多边形边界提取](https://blog.csdn.net/qq_36686437/article/details/120125699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [凸包+凹包+凸边凹化算法](https://blog.csdn.net/qingtianhaoshuai/article/details/122245165)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值