zoukankan      html  css  js  c++  java
  • 判断任意多边形与矩形的相交(线段与矩形相交或线段与线段相交)

    任意多边形与矩形的相交,其实就是判断多条线段是否与这个矩形相交,再简单点就是判断线段是否与矩形的每一条边相交了。那现在,我们先来看看判断一条线段与矩形的其中一条线段的相交的情况(上方水平线):

    (图形中的a/b=d/c如果看不明白的,我也无语了,回去翻下几何图形的那中学课本。)图中已知的是红色的线段,就是要检测的线段,其起点就是star,重点是end,其坐标是已知的。图中的蓝色线是辅助理解的线。而图中的y0,x1,x2是矩形的其中一条线的参数,那,下面我们从代码去理解一下:

            public static bool PointInLine(Vector2 point, Vector2[] vertices, float accuracy)
            {
                
    if (point == null || (vertices == null || vertices.Length < 2))
                    
    return false;
                Rect pointRect 
    = new Rect();
                pointRect.Width 
    = accuracy*2;
                pointRect.Height 
    = accuracy*2;
                pointRect.Center 
    = point;

                
    return LineIntersectRect(vertices, pointRect);
            }

    其中point参数就是要检测的点,vertices是被检测的线段集合,accuracy是其精确性,如果为0,则精确到点,如果存在一定的模糊性,可以给这个参数赋值。(做图形开发的时候,鼠标点要精确到某条线上,那要求太高了,因此,一般会设置其精确值,这个值越小越精确。)从这段代码中可以理解成N条线段vertices去检测是否与这个存在一定精确性的矩形相交,那下面看看这个函数LineIntersectRect的代码:

            public static bool LineIntersectRect(Vector2[] vertices, Rect rect)
            {
                
    if ((vertices == null || vertices.Length < 2|| rect == Rect.Empty)
                    
    return false;
                
    for (int i = 0; i < vertices.Length - 1; i++)
                {
                    
    if (CheckRectLine(vertices[i], vertices[i + 1], rect))
                        
    return true;
                }
                
    return false;
            }

    这段代码很好理解,就是从数组中每2点去检测其是否与这个矩形相交。下面就看看CheckRectLine检测其线段与矩形检测的代码:

            private static bool CheckRectLine(Vector2 start, Vector2 end, Rect rect)
            {
                
    bool result = false;
                
    if (rect.Contains(start) || rect.Contains(end))
                    result 
    = true;
                
    else
                {
                    result 
    |= CheckRectLineH(start, end, rect.LeftTop.Y, rect.LeftTop.X, rect.RightBottom.X);
                    result 
    |= CheckRectLineH(start, end, rect.RightBottom.Y, rect.LeftTop.X, rect.RightBottom.X);
                    result 
    |= CheckRectLineV(start, end, rect.LeftTop.X, rect.LeftTop.Y, rect.RightBottom.Y);
                    result 
    |= CheckRectLineV(start, end, rect.RightBottom.X, rect.LeftTop.Y, rect.RightBottom.Y);
                }
                
    return result;
            }

    线段与矩形是否相交的方法就变成了线段与矩形的4条边是否相交进行检测。这里,该方法CheckRectLineH是水平方向上的检测,就是检测矩形的上边线与下边线,那CheckRectLineV就是检测矩形的左边线和右边线了。我们先来看下CheckRectLineH这个函数的代码,然后再看看图来进行分析:

            private static bool CheckRectLineH(Vector2 start, Vector2 end, float y0, float x1, float x2)
            {
                
    //直线在点的上方
                if ((y0 < start.Y) && (y0 < end.Y))
                    
    return false;
                
    //直线在点的下方
                if ((y0 > start.Y) && (y0 > end.Y))
                    
    return false;
                
    //水平直线
                if (start.Y == end.Y)
                {
                    
    //水平直线与点处于同一水平。
                    if (y0 == start.Y)
                    {
                        
    //直线在点的左边
                        if ((start.X < x1) && (end.X < x1))
                            
    return false;
                        
    //直线在x2垂直线右边
                        if ((start.X > x2) && (end.X > x2))
                            
    return false;
                        
    //直线的部分或者全部处于点与x2垂直线之间
                        return true;
                    }
                    
    else//水平直线与点不处于同一水平。
                    {
                        
    return false;
                    }
                }
                
    //斜线
                float x = (end.X - start.X) * (y0 - start.Y) / (end.Y - start.Y) + start.X;
                
    return ((x >= x1) && (x <= x2));
            }

    看完代码,我们再看最开始时候的图,协助分析,我们可以想到这个函数其实就是检测X1与Y0的交点与X2与Y0的交点是否与红色线段是否相交,如果相交,则判断其交点是否在x1与x2之间的范围。(根据调用的参数,可以知道其正好就是上下边线)我们根据图的理解,可以得出:

    a=(end.X - start.X),b=(end.Y - start.Y),c=(y0 - start.Y),d=start.X+x。从而可以得出

    x = (end.X - start.X) * (y0 - start.Y) / (end.Y - start.Y) + start.X。

    同理,CheckRectLineV的分析刚好是x和Y倒转过来。下面贴出代码:(分析就不再细说了)

            private static bool CheckRectLineV(Vector2 start, Vector2 end, float x0, float y1, float y2)
            {
                
    if ((x0 < start.X) && (x0 < end.X))
                    
    return false;
                
    if ((x0 > start.X) && (x0 > end.X))
                    
    return false;
                
    if (start.X == end.X)
                {
                    
    if (x0 == start.X)
                    {
                        
    if ((start.Y < y1) && (end.Y < y1))
                            
    return false;
                        
    if ((start.Y > y2) && (end.Y > y2))
                            
    return false;
                        
    return true;
                    }
                    
    else
                    {
                        
    return false;
                    }
                }
                
    float y = (end.Y - start.Y) * (x0 - start.X) / (end.X - start.X) + start.Y;
                
    return ((y >= y1) && (y <= y2));
            }

    备注:这里由于是用Vortex2D的源码进行分析的,其中Vector2其实就是point的类型,只是该Vector2封装了更多的方法而已。其Rect的类型也不是系统的Rect,也是Vortex2D自己封装了更多方法的Rect类型,如果大家在使用该部分代码时,有编译出错的情况,请注意自行修改。这里提出的是一个思路。懂得了这个思路,自然就懂得如何修改各自的代码。

    原创作品出自努力偷懒,转载请说明文章出处http://www.cnblogs.com/kfarvid/

  • 相关阅读:
    Balanced Binary Tree
    Swap Nodes in Pairs
    Reverse Nodes in k-Group
    Reverse Linked List II
    Remove Nth Node From End of List
    Remove Duplicates from Sorted List II
    Remove Duplicates from Sorted List
    Partition List
    Merge Two Sorted Lists
    【Yii2.0】1.2 Apache检查配置文件语法
  • 原文地址:https://www.cnblogs.com/wangzihao/p/2197875.html
Copyright © 2011-2022 走看看