判断点是否在多边形内

using System.Collections;
using System;
using Utility;

public class Line
{
    public enum ExpressionType
    {
        ETYPE_NULL = 0,
        ETYPE_NORMAL = 1,   //一般式
        ETYPE_KXY = 2,      //点斜式
    }
    private ExpressionType _Type = ExpressionType.ETYPE_NULL;

    private float _Def_A = 1;
    private float _Def_B = 0;
    private float _Def_C = 0;

    private Point _Def_Pos = new Point(0,0);    //某一坐标点
    private float _Def_K = 0;                       //斜率
    private bool _isVerticalWithX = false;          //垂直与x轴斜率不存在 _Def_K 没有意义

    //一般式
    public Line(float A, float B, float C)
    {
        if (A == 0 && B == 0)
        {
            throw new Exception("can't to create a line");
        }
        _Def_A = A;
        _Def_B = B;
        _Def_C = C;
        _Type = ExpressionType.ETYPE_NORMAL;
    }
    //点斜式
    public Line(float k, Point pos, bool isVerticalWithX = false)
    {
        _Type = ExpressionType.ETYPE_KXY;
        _Def_Pos = pos.Clone();
        _Def_K = k;
        _isVerticalWithX = isVerticalWithX;
    }

    public static Line GetLine(float A, float B, float C)
    {
        Line TLine = null;
        try
        {
            TLine = new Line(A, B, C);
        }
        catch (System.Exception ex)
        {
            return null;
        }
        return TLine;
    }

    public static Line GetLine(float k, Point pos, bool isVerticalWithX = false)
    {
        return new Line(k, pos, isVerticalWithX);
    }

    //判断一个点是否在直线上
    public bool CheckPointIsInLine(Point pos)
    {
        if (_Type == ExpressionType.ETYPE_NORMAL)
        {
            if (((_Def_A * pos.x) + (_Def_B * pos.y) + _Def_C) == 0)
            {
                return true;
            }
        }
        else if (_Type == ExpressionType.ETYPE_KXY)
        {
            if ((_Def_K * (pos.x - _Def_Pos.x)) == (pos.y - _Def_Pos.y))
            {
                return true;
            }
        }

        return false;
    }
    //得到 x 确定 求y
    public Point getInLinePointByX(float x)
    {
        Point point = new Point();
        point.x = x;
        if (_Type == ExpressionType.ETYPE_NORMAL)
        {
            if (_Def_B == 0)    //无数种情况 或一种也不符合
                return null;
            point.y = (-_Def_C - _Def_A * x) / _Def_B;
        }
        else if (_Type == ExpressionType.ETYPE_KXY)
        {
            if (_isVerticalWithX)
                return null;
            point.y = _Def_K * (x - _Def_Pos.x) + _Def_Pos.y;
        }
        return point;
    }
    //得到 y 确定 求 x
    public Point getInLinePointByY(float y)
    {
        Point point = new Point();
        point.y = y;
        if (_Type == ExpressionType.ETYPE_NORMAL)
        {
            if (_Def_A == 0f)    //无数种情况 或一种也不符合
                return null;
            point.x = (-_Def_C - _Def_B * y) / _Def_A;
        }
        else if (_Type == ExpressionType.ETYPE_KXY)
        {
            if (_Def_K == 0f)
                return null;
            point.x = (y - _Def_Pos.y) / _Def_K + _Def_Pos.x;
        }
        return point;
    }

    //判断线段是否与直线相交
    //原理:线段2端(x1,y1)和(x2,y2),直线方程f(x,y)=0,则f(x1,y1)*f(x2,y2)<=0时线段和直线相交
    public LineWithSegmentRetins CheckSegmentIsCrossLine(Segment segment)
    {
        if (segment == null)
            return LineWithSegmentRetins.LSR_Independent;
        float flag0 = 1;
        float flag1 = 1;
        if (_Type == ExpressionType.ETYPE_NORMAL)
        {
            flag0 = (_Def_A * segment._start.x) + (_Def_B * segment._start.y) + _Def_C;
            flag1 = (_Def_A * segment._end.x) + (_Def_B * segment._end.y) + _Def_C;
        }
        else if (_Type == ExpressionType.ETYPE_KXY)
        {
            flag0 = _isVerticalWithX ? segment._start.x - _Def_Pos.x : _Def_K * (segment._start.x - _Def_Pos.x) - (segment._start.y - _Def_Pos.y);
            flag1 = _isVerticalWithX ? segment._end.x - _Def_Pos.x : _Def_K * (segment._end.x - _Def_Pos.x) - (segment._end.y - _Def_Pos.y);
        }
        if(flag0 == 0 && flag1 == 0)
            return LineWithSegmentRetins.LSR_Cross_Coincide;
        else if(flag0 == 0 || flag1 == 0)
            return LineWithSegmentRetins.LSR_Cross_In;
        else if(flag0 * flag1 < 0)
            return LineWithSegmentRetins.LSR_Cross;
        return LineWithSegmentRetins.LSR_Independent;
    }


    public ExpressionType GetType()
    {
        return _Type;
    }

    public bool GetXYKLineIsVerticalWithX()
    {
        return (_Type == ExpressionType.ETYPE_KXY) && _isVerticalWithX;
    }
}
using System.Collections;
using Utility;
using System;

public class Segment {
    public Point _start;
    public Point _end;
    public Segment(Point start, Point end)
    {
        if (start == end)
        {
            throw new Exception("can't to create a segment");
        }

        _start = start.Clone();
        _end = end.Clone();
    }

    public static bool CheckSegment(Point start, Point end)
    {
        return !(start == end);
    }

    public bool CheckPointIsInSegment(Point point)
    {
        return Utility.Utility.CrossMultiply(point, _start, _end) == 0f && (point.x >= Utility.Utility.getMin(_start.x, _end.x) && point.x <= Utility.Utility.getMax(_start.x, _end.x));
    }

}
using System.Collections;
using System.Collections.Generic;
using System;
using Utility;
/// <summary>
///  多边形
/// </summary>
public class Polygon {

    public List<Point> _Pos_list = new List<Point>();
    public Polygon(List<Point> pos_list, bool checkLegal = true)
    {
        if(pos_list.Count < 3)
        {
            throw new Exception("can't to create a polygon");
        }

        if(checkLegal && !ChackIsPolygon(pos_list))
            throw new Exception("can't to create a polygon");
        else if(!checkLegal)
        {
            //三个点组成的直线
            //即使三个点在一条直线上,也认为是一个多边形
            //组成的多边形有交叉,是多个多变形的组合, 也认为是一个多边形
        }

        _Pos_list.Clear();
        for(int index = 0; index < pos_list.Count; index++)
        {
            _Pos_list.Add(pos_list[index].Clone());
        }
    }

    //组成矩形的边不能重合, 不能与第三条边相交
    public static bool ChackIsPolygon(List<Point> pos_list)
    {
        if (pos_list.Count < 3)
            return false;
        List<Segment> segment_list = new List<Segment>();
        //提取线段
        for (int index = 0; index < pos_list.Count; index++)
        {
            segment_list.Add(new Segment(pos_list[index].Clone(), pos_list[index == pos_list.Count - 1 ? 0 : index + 1].Clone()));
        }
        //判断相交
        for (int x = 0; x < segment_list.Count; x++)
        {
            for (int y = x + 1; y < segment_list.Count; y++)
            {
                SegmentsRelations flag = Utility.Utility.CheckSegmentsIntersect(segment_list[x], segment_list[y]);
                //当 当前线段与不相邻的线段相交 肯定不符合
                //当矩形的2条相邻边重合和这两条边的另外两条领边必有一条边与其重合的边中非领边的边有焦点
                if ((y - x > 1 && !(x == 0 && y == segment_list.Count - 1)) && flag > SegmentsRelations.SR_Independent)
                    return false;                
            }
        }
        return true;
    }

    public List<Segment> getSegments()
    {
        List<Segment> segment_list = new List<Segment>();
        //提取线段
        for (int index = 0; index < _Pos_list.Count; index++)
        {
            segment_list.Add(new Segment(_Pos_list[index].Clone(), _Pos_list[index == _Pos_list.Count - 1 ? 0 : index + 1].Clone()));
        }
        return segment_list;
    }
}
using System.Collections;
using System.Collections.Generic;
using System;

namespace Utility
{
    //2条线段之间的关系
    public enum SegmentsRelations
    {
        SR_Independent = -1,            //不相交
        SR_Cross = 0,                   //相交
        SR_Cross_In = 1,                //相交 一个点重合
        SR_Cross_Coincide = 3,          //相交 共线
    }

    //直线和线段之间的关系
    public enum LineWithSegmentRetins
    {
        LSR_Independent = -1,           //不相交
        LSR_Cross = 0,                  //相交
        LSR_Cross_In = 1,               //相交 线段 一个点重合 
        LSR_Cross_Coincide = 3,         //相交 重合
    }

    public class Point
    {
        public Point()
        {

        }
        public Point(float tx, float ty)
        {
            x = tx;
            y = ty;
        }
        public float x = 0;
        public float y = 0;

        public Point Clone()
        {
            return new Point(x,y);
        }

        public string ToString()
        {
            return "Point( " + x.ToString() + " , " + y.ToString() + " )"; 
        }
    }    

    public static class Utility
    {
        public static float getAbs(float src)
        {
            return src < 0 ? (-src) : src;
        }

        public static float getMax(float arg0, float arg1)
        {
            return arg0 > arg1 ? arg0 : arg1;
        }
        public static float getMin(float arg0, float arg1)
        {
            return arg0 > arg1 ? arg1 : arg0;
        }
        /*---------------------*/
        // 判断直线是否与线段相交
        /*---------------------*/
        public static LineWithSegmentRetins CheckSegmentIsCrossLine(Line line, Segment segment)
        {
            if (line == null || segment == null)
                return LineWithSegmentRetins.LSR_Independent;

            return line.CheckSegmentIsCrossLine(segment);
        }

        public static bool CheckPointIsInSegment(Segment se, Point point)
        {
            if (se == null || point == null)
                return false;
            return se.CheckPointIsInSegment(point);
        }

        /*---------------------------------------------------------------------------*/
        //  
        //                      判断两个边垂直与x轴或者y轴的矩形是否相交
        //
        //  参数为 矩形的对角线 端点
        //  原理 两个矩形的中心点在x轴,y轴上的距离 都小于等于 两个矩形的宽或者高的一半之和
        //  即使对角线垂直x轴或者y轴 不影响逻辑判断
        /*---------------------------------------------------------------------------*/
        public static bool CheckRectanglesIntersect(Point Rect0_p0, Point Rect0_p1, Point Rect1_p0, Point Rect1_p1)
        {
            /*float Halfheight = (getAbs(Rect0_p0.y - Rect0_p1.y) + getAbs(Rect1_p0.y - Rect1_p1.y)) / 2.0f;
            float Halfwide = (getAbs(Rect0_p0.x - Rect0_p1.x) + getAbs(Rect1_p0.x - Rect1_p1.x)) / 2.0f;
            float centerdistanceX = getAbs((Rect0_p0.x + Rect0_p1.x)  - (Rect1_p0.x + Rect1_p1.x))/ 2f;
            float centerdistanceY = getAbs((Rect0_p0.y + Rect0_p1.y)  - (Rect1_p0.y + Rect1_p1.y)) / 2f;
            if (centerdistanceX <= Halfwide && centerdistanceY <= Halfheight)
                return true;*/
            //简化为
            if (getAbs((Rect0_p0.x + Rect0_p1.x) - (Rect1_p0.x + Rect1_p1.x)) <= getAbs(Rect0_p0.x - Rect0_p1.x) + getAbs(Rect1_p0.x - Rect1_p1.x) && getAbs((Rect0_p0.y + Rect0_p1.y) - (Rect1_p0.y + Rect1_p1.y)) <= getAbs(Rect0_p0.y - Rect0_p1.y) + getAbs(Rect1_p0.y - Rect1_p1.y))
                return true;
            return false;
        }        

        /*********************************************************************************************
         *              
         *                                  判断 2条线段是否相交
         *              
         *  原理 1快速排除 (排除2条线段在同一直线上时 不相交)
         *       2 两条线段相交 必然相互 相交, 两条线段的两个端点各自在另一线段的两侧或有一端点在另一线段上
         *
        **********************************************************************************************/
        public static SegmentsRelations CheckSegmentsIntersect(Segment s0, Segment s1)
        {
            if (!CheckRectanglesIntersect(s0._start, s0._end, s1._start, s1._end))
                return SegmentsRelations.SR_Independent;
            //跨立原理
            /***********************************************************************************************************************************
             * 向量的叉乘 几何意义垂直与两向量的向量(实际运算得出的是伪向量,绝对值为垂直与两向量的模,也等于以两向量为2边组成的平行四边形的面积)
             * 设向量P(A1,A2) 向量Q (B1,B2)
             * P X Q = -(Q X P)
             * P X Q = A1*B2 - A2*B1   //(A1*B2 - A2*B1 的绝对值为平行四边形的面积)
             * 右手原理 右手半握,大拇指垂直向上,四指右向量P握向Q,大拇指的方向就是叉积的方向
             * 当 A1*B2 - A2*B1  大于 0 时 P 在 Q的顺时针方向 (右手原理 )
             * 
             *   !!!!!!!!!!!!!以上大一数学有学!!!!!!!!!!!!!!
             * 
             * 当P X Q > 0   //P在Q的顺时针方向 拇指向下
             * 当P X Q < 0   //P在Q的逆时针方向 拇指向上
             * 当P X Q = 0   //p Q 共线 方向不确定是否相同 拇指范围为垂直与共线的平面的圆
             * 
             *  p 跨立 Q  (A1 - B1) X (B2 - B1)   (A2 - B1) X (B2 - B1)  得出的值正负相反时 跨立
             *  所以 (A1 - B1) X (B2 - B1) *(A2 - B1) X (B2 - B1) < 0 当 = 0 时 因为已通过快速排除 2线段必然重合
             *  简化为 (A1 - B1) X (B2 - B1) * (B2 - B1) X (A2 - B1) >= 0
             ***********************************************************************************************************************************/
            float flag0 = CrossMultiply(s0._start, s1._end, s1._start);
            float flag1 = CrossMultiply(s1._end, s0._end, s1._start);
            if (flag0 == 0f && flag1 == 0f)
                return SegmentsRelations.SR_Cross_Coincide;
            else if (flag0 == 0 || flag1 == 0)
            {
                if (s0.CheckPointIsInSegment(s1._start) || s0.CheckPointIsInSegment(s1._end) || s1.CheckPointIsInSegment(s0._start) || s1.CheckPointIsInSegment(s0._end))
                    return SegmentsRelations.SR_Cross_In;
                else
                    return SegmentsRelations.SR_Independent;
            }
            else if (flag0 * flag1 > 0)
            {
                float flag2 = CrossMultiply(s1._start, s0._end, s0._start);
                float flag3 = CrossMultiply(s0._end, s1._end, s0._start);
                if (flag2 * flag3 > 0)
                    return SegmentsRelations.SR_Cross;
                else
                    return SegmentsRelations.SR_Independent;
            }
            return SegmentsRelations.SR_Independent;
        }
        //向量 AC BC的叉乘
        public static float CrossMultiply(Point A, Point B, Point C)
        {
            return (A.x - C.x) * (B.y - C.y) - (A.y - C.y) * (B.x - C.x);
        }

        /******************************************************************
         *
         *                      判断一个点是否在多边形内  
         * 
         *    以该点做平行与x轴直线 当且仅当 直线在点两侧与矩形的交点个数为奇数时
         *                          该点在多边形内
         *                          
         *     当一侧的交点为奇数时,另一侧肯定也为奇数 所以只用证明一侧即可
         * 
        ******************************************************************/
        public static bool CheckPointInPolygon(Point point, Polygon polygon)
        {
            if (point == null || polygon == null)
                return false;
            // 排除 点在多边形边山的情况
            List<Segment> segment_list = polygon.getSegments();
            for (int index = 0; index < segment_list.Count; index++)
            {
                if (segment_list[index].CheckPointIsInSegment(point))
                    return true;
            }
            // 直线 y = point.y
            Line lineX = new Line(0f,1f,-point.y);
            //检测右边
            int crossCount = 0;
            for (int index = 0; index < segment_list.Count; index++)
            {
                LineWithSegmentRetins type = CheckSegmentIsCrossLine(lineX, segment_list[index]);
                if (type == LineWithSegmentRetins.LSR_Independent || type == LineWithSegmentRetins.LSR_Cross_Coincide)
                {
                    //不相交 和 重合的边不算在交点计数里面
                    continue;
                }
                else if (type == LineWithSegmentRetins.LSR_Cross)
                {
                    //判断交点是否在右侧 判断线段两端点与 point 的向量是否在 以线段所在的向量的 夹角

                    Point hPoint = null;
                    Point lPoint = null;
                    if (segment_list[index]._start.y > segment_list[index]._end.y)
                    {
                        hPoint = segment_list[index]._start;
                        lPoint = segment_list[index]._end;
                    }
                    else
                    {
                        hPoint = segment_list[index]._end;
                        lPoint = segment_list[index]._start;
                    }
                    if (CrossMultiply(point, lPoint, hPoint) > 0f)
                    {
                        crossCount++;
                    }
                }
                else if (type == LineWithSegmentRetins.LSR_Cross_In)
                {

                    //在 point y 轴上面的不计数  
                    if ((segment_list[index]._start.y == point.y && segment_list[index]._end.y < point.y) || (segment_list[index]._end.y == point.y && segment_list[index]._start.y < point.y))
                    {
                        //判断右侧
                        Point hPoint = null;
                        Point lPoint = null;
                        if (segment_list[index]._start.y > segment_list[index]._end.y)
                        {
                            hPoint = segment_list[index]._start;
                            lPoint = segment_list[index]._end;
                        }
                        else
                        {
                            hPoint = segment_list[index]._end;
                            lPoint = segment_list[index]._start;
                        }
                        if (CrossMultiply(point, lPoint, hPoint) > 0f)
                        {
                            crossCount++;
                        }
                    }
                }

            }
            return crossCount % 2 == 1;
        }        
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值