zoukankan      html  css  js  c++  java
  • 平面直角坐标系点坐标与多边形位置判断

      受到在线变成QQ群讨论的一个问题启发,“如何判断点是否在三角形内部?”

      给定一个平面直角坐标系和一组数据,数据由一系列坐标点组成,形式如:(xi, yi),i = 1, 2, 3……, n; 给定的(xi, yi)可以组成一个封闭的多边形,数组下标顺序连接点坐标,(x0, y0) -> (x1, y1) -> ……->(xn-1, yn-1) ->(x0, y0),现在给定一个坐标点(X, Y),请判断该点的位置:返回 表示在多边形内部,返回 表示在多边形边上,返回 -1 表示在多边形外部。

     

    思路1:分割法

             假设n = 3, (xi, yi)i=1,2,3; 三个点坐标为(0, 0), (0, 1), (1, 0),那么该三角形围成的三条直线的解析式分别为:y = 0;  x = 0;  y = -x + 1;还记得高中时我们学的不等式方程吗,如图所示:

       

      如果点(X0, Y0)在上图中封闭的三角形内部,那么满足三个不等式:

        X0 > 0; Y0 > 0; X0 + Y0 -1 < 0; 若在三角形边上则是三个不等式转换为相应的等式:

        X0 = 0; Y0 = 0; X0 + Y0 -1 = 0; 其余情况则是点在三角形外部;

          通过上面两个例子,解析式用aixi + biyi + ci = 0表示,可以得到结论,则有当n 为3时,满足三个对应的不等式。

      在解题的过程中,发现对于任意多边形来说,需要讨论的情况太复杂,所以在这里我们假设该多边形是一个凸多边形。所谓凸多边形,就是把一个多边形任意一边向两方无限延长成为一条直线,如果多边形的其他各边均在此直线的同旁,那么这个多边形就叫做凸多边形。

      在多边形内部找到一个相对合适的参照坐标点(X, Y),以该点为中心,分别与多边形各点连接,形成n个独立的三角形,通过上面对点与三角形位置判断的理论,可以分n次判断该点是否在分割的n个独立的三角形内部即可;如下图所示:

      找到比较合适的点的方法是:在任意两个内角之间,作这两个内角的角平分线,角平分线的交点的坐标就是我们所要找的参照点。

      正当我要实现其代码时,在网上发现了一个更加简洁的解决方法,并且,此方法对于多边形没有限制,适用于任意多边形的情况。

    思路2:向量叉积法

      设矢量P = (x1,y1) ,Q = (x2,y2)

      则矢量叉积定义为:  P × Q = x1*y2 – x2*y1   得到的是一个标量

      显然有性质 P × Q = – ( Q × P )   P × ( – Q ) = – ( P × Q )

      如不加说明,下面所有的点都看作矢量,点的乘法看作矢量叉积;

      叉乘的重要性质:

      若 P × Q  > 0 ,  则P 在Q的顺时针方向

      若 P × Q  < 0 ,  则P 在Q的逆时针方向

      若 P × Q  = 0 ,  则P 与Q共线,但可能同向也可能反向 ,用于判断点是否在边上

      对于(x0, y0), (x1, y1), ……, (xn - 1, yn - 1)这些坐标,若(xk, yk)指向(xk + 1, yk + 1)的矢量为V,(xk, yk)指向(X, Y)的矢量为Vf对于所有的k = 0, 1, 2……,n - 1,当k = n - 1时,Vn- 1 (xn - 1, yn - 1)指向(x0, y0)

      1、任意一点(xk, yk)使得Vf若为(0, 0)零向量,则点在多边形上;

      2、若满足Vf在Vk的同一方向(均为逆时针或者顺时针方向),即都满足P × Q  < 0(顺时针为P × Q  > 0),则点(X, Y)在该多边形内部;

      3、若不在同一方向且Vf和Vk永远不共线,则在多边形外部;

      4、若Vf和Vk存在共线情况,则判断使得Vf和Vk共线的点(X, Y)是否在多边形上(Vf  。Vk  >0 且|Vf | < |Vk|则点

    (X, Y)在多边形边上),其他则在外部。

      通过这种方法,很好判断点与任意多边形的位置关系,而且复杂度并不高,C语言代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    //返回1表示内部,0表示边上,-1表示外部
    int dotLocation(const double *x, const double *y, int n, const double XG, const double YG)
    {
        int currentflag = 0;  //当前状态,根据每次叉乘的值来判断
        int lastflag = 0;  //只在第一次进入循环式赋值,后面依次比较lastflag和currentflag的关系
        int iterN = 0;
        double vectorPX = 0.0, vectorPY = 0.0;
        double vectorQX = 0.0, vectorQY = 0.0;
        double PRODUCT = 0.0;//叉乘
        for(iterN = 0; iterN < n; iterN++)
        {
            vectorPX = XG - x[iterN];
            vectorPY = YG - y[iterN];
            if(vectorPX == 0 && vectorPY == 0) return 0;  //要检测的点若在数组x, y中,表示该点在多边形上
            vectorQX = x[(iterN == n - 1) ? 0 : (iterN + 1)] - x[iterN];
            vectorQY = y[(iterN == n - 1) ? 0 : (iterN + 1)] - y[iterN];
            PRODUCT = vectorPX * vectorQY - vectorQX * vectorPY;//计算叉乘
    
            currentflag = ( PRODUCT > 0) ? 1 : ((PRODUCT < 0)? -1 : 0);//判断叉乘获取currentflag的值
    
            if(iterN == 0)//第一次进入循环
            {
                lastflag = currentflag;
            }
            if(currentflag != lastflag)
            {
                switch(currentflag)
                {
                case 0:
                    {
                        //叉乘为0时,共线(反向货同向),要分两种情况:1、点在边上;2、点在外部
                        return (vectorPX * vectorQX + vectorPY * vectorQY > 0 && (vectorPX * vectorPX + vectorPY * vectorPY) <= (vectorQX * vectorQX + vectorQY * vectorQY))? 0 : -1;
                    }
                default:
                    return -1;//点在多边形外部
                }
            }
        }
        return 1;  //点在多边形内部
    }
    int main()
    {
    //    double x[3] = { 0.0, 0.0, 1.0 };
    //    double y[3] = { 0.0, 1.0, 0.0 };
    //    int n = 3;
    //    double X , Y;
    //    X = 0.5;
    //    Y = 0.25;
        double x[4] = { 0.0, 0.0, 1.0, 1.0 };
        double y[4] = { 0.0, 1.0, 1.0, 0.0 };
        int n = 4;
        double X , Y;
        X = 0.6;
        Y = 0.6;
        int result = dotLocation(x, y ,n, X ,Y);
        printf("the result is %d\n", result);
        switch(result)
        {
            case 0: printf("该点在多边形的边上!\n"); break;
            case 1: printf("该点在多边形的内部!\n"); break;
            case -1: printf("该点在多边形的外部!\n"); break;
            default: break;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    SpringBoot 发送邮件
    @Component 爆红
    Java 调用OpenCV获取图像数据,发送Redis并从Redis获取数据显示
    xxx.bat windows Bat文件启动cmd命令运行jar包
    队列
    paramiko连接服务器
    matplotlib模块画坐标图
    获取当前时间
    jsonpath和打印模板
    提取图片中的文字
  • 原文地址:https://www.cnblogs.com/bestDavid/p/dotLocation.html
Copyright © 2011-2022 走看看