Unity 面试题汇总(六)常用的一些几何算法

Unity 面试题汇总(六)常用的一些几何算法

目录

Unity 面试题汇总(六)常用的一些几何算法

1、怎么判断一个点是否在直线上

2、判断点是否在线段上

3、判断点与线的位置关系

4、计算点在直线上的投影(向量投影)

5、计算点到直线距离

6、计算点到线段的距离

7、判断多边形是否为凸多边形

8、判断线段与线段是否共线

9、判断线段与线段是否重合(非相交)

10、线段与线段是否相交

11、计算直线与直线的交点

12、线段与线段的交点

13、射线与射线是否相交,以及交点

15.点围绕另一点旋转指定角度

16、点是否在任意多变内

17、点是否在椭圆内

18、直线与椭圆的交点计算


1、怎么判断一个点是否在直线上

已知点P(x,y),以及直线上的两点A(x1,y1)、B(x2,y2),可以通过计算向量AP与向量AB的叉乘是否等于0来计算点P是否在直线AB上

知识点:叉乘

    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }
    

    /// <summary>
    /// 点是否在直线上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
    }

2、判断点是否在线段上

已知点P(x,y),以及线段A(x1,y1),B(x2,y2)。

1)方法一

可以进行下面两部来判断点P是否在线段AB上:

(1)点是否在线段AB所在的直线上(点是否在直线上)
(2)点是否在以线段AB为对角线的矩形上,来忽略点在线段AB延长线上

    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }
    

    /// <summary>
    /// 点是否在直线上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
    }


    /// <summary>
    /// 点是否在线段上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnSegment(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        //1.先通过向量的叉乘确定点是否在直线上
        //2.在拍段点是否在指定线段的矩形范围内
        if (IsPointOnLine(point,lineStart,lineEnd))
        {
            //点的x值大于最小,小于最大x值 以及y值大于最小,小于最大
            if (point.x >= Mathf.Min(lineStart.x, lineEnd.x) && point.x <= Mathf.Max(lineStart.x, lineEnd.x) &&
                point.y >= Mathf.Min(lineStart.y, lineEnd.y) && point.y <= Mathf.Max(lineStart.y, lineEnd.y))
                return true;
        }
        return false;
    }

2)方法二

计算向量AP的长度加上向量BP的长度是否等于向量AB的长度,来确定点P是否在线段AB上

    /// <summary>
    /// 点是否在线段上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnSegment2(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        return Mathf.Approximately(Mathf.Abs((lineStart - point).magnitude) + Mathf.Abs((lineEnd - point).magnitude),
                        Mathf.Abs((lineEnd - lineStart).magnitude));
    }

3、判断点与线的位置关系

已知点P(x,y),与直线上A(x1,y1),B(x2,y2)两点,通过向量AP与BP的叉乘返回的结果,即可确定点在直线的位置关系。

判断依据:1)等于0:点在直线上;2)小于0:点在直线的左侧;3)大于0:点在直线的右侧

    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }


    /// <summary>
    /// 点与线的位置关系
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns>==0:点在线上 <0:点在线的左侧 >0:点在线的右侧</returns>
    public static int IsPointToLinePosition(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float crossValue = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        if (crossValue < 0) return -1;
        if (crossValue > 0) return 1;
        return 0;
    }

4、计算点在直线上的投影(向量投影)

已知点P(x,y),与直线上两点A(x1,y1),B(x2,2),求P点在直线AB上的投影

知识点:通过向量投影公式(点乘),即可获得点P在直线AB投影点。

    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }
    

    /// <summary>
    /// 点是否在直线上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
    }
    

    /// <summary>
    /// 点到直线上的投影坐标
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static Vector2 Point2LineProject(Vector2 point,Vector2 lineStart,Vector2 lineEnd)
    {
    	if (IsPointOnLine(point,lineStart,lineEnd))
            return point;
        Vector2 v = point - lineStart;
        Vector2 u = lineEnd - lineStart;
        //求出u'的长度
        float u1Length = Vector2.Dot(u, v) / u.magnitude;
        return u1Length * u.normalized + lineStart;
    }

5、计算点到直线距离

已点P(x,y),与直线上两点A(x1,y1),B(x2,y2),求点P到直线AB的距离

1)方法1:
先求出点P在直线AB上的投影点P’,求出点P与点P’的距离即可。

    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }
    
    /// <summary>
    /// 点是否在直线上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
    }

    /// <summary>
    /// 点到直线上的投影坐标
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static Vector2 Point2LineProject(Vector2 point,Vector2 lineStart,Vector2 lineEnd)
    {
    	if (IsPointOnLine(point,lineStart,lineEnd))
            return point;
        Vector2 v = point - lineStart;
        Vector2 u = lineEnd - lineStart;
        //求出u'的长度
        float u1Length = Vector2.Dot(u, v) / u.magnitude;
        return u1Length * u.normalized + lineStart;
    }

    /// <summary>
    /// 点到直线的距离
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static float Point2LineDistance(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        return Vector2.Distance(point, Point2LineProject(point, lineStart, lineEnd));
    }

2)方法2:
通过海伦公式+底x高三角形面积公式,就可求得点P到直线AB的距离。
海伦公式:知道三角形3条边长,求三角形面积。链接:海伦公式
底x高三角形面积公式:底x高=2倍的三角形面积


    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }
    
    /// <summary>
    /// 点是否在直线上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
    }    

    /// <summary>
    /// 点到直线的距离
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static float Point2LineDistance(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        //先判断点是否在线上
        if (IsPointOnLine(point, lineStart, lineEnd)) return 0;
        //通过海伦公式求得三角形面积,然后面积除以底,得到高度
        //point 简写为p lineStart简写为s lineEnd简写为e
        float se = Vector2.Distance(lineStart, lineEnd);
        float sp = Vector2.Distance(lineStart, point);
        float ep = Vector2.Distance(lineEnd, point);
        
        //海伦公式 + 底x高面积公式
        //半周长
        float p = (se + sp + ep) / 2;
        //求面积
        float s = Mathf.Sqrt(p * (p - se) * (p - sp) * (p - ep));
        //面积除以底得高度
        return 2 * s / se;
    }

6、计算点到线段的距离

已知点P(x,y),与线段A(x1,y1),B(x2,y2),计算点到线段的距离

思路:先求出点到直线AB(包括线段AB的延长线)的投影点,在判断点是否在线段AB的矩形区间内,在计算两点的距离即可。

    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }
    
    /// <summary>
    /// 点是否在直线上
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
    }

    /// <summary>
    /// 点到直线上的投影坐标
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static Vector2 Point2LineProject(Vector2 point,Vector2 lineStart,Vector2 lineEnd)
    {
    	if (IsPointOnLine(point,lineStart,lineEnd))
            return point;
        Vector2 v = point - lineStart;
        Vector2 u = lineEnd - lineStart;
        //求出u'的长度
        float u1Length = Vector2.Dot(u, v) / u.magnitude;
        return u1Length * u.normalized + lineStart;
    }

    /// <summary>
    /// 点到线段的距离
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns></returns>
    public static float Point2SegmentDistance(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        Vector2 projectPoint = Point2LineProject(point,lineStart,lineEnd);
        if (projectPoint.x >= Mathf.Min(lineStart.x, lineEnd.x) &&
            projectPoint.x <= Mathf.Max(lineStart.x, lineEnd.x) &&
            projectPoint.y >= Mathf.Min(lineStart.y, lineEnd.y) && projectPoint.y <= Mathf.Max(lineStart.y, lineEnd.y))
            return Vector2.Distance(point, projectPoint);
        return float.MaxValue;
    }

7、判断多边形是否为凸多边形

凸(凹)多边形 计算方法
已知多边形的逆时针顶点序列,依次判断相邻两个线段走向是否一致即可。

(点与线段位置关系,因为规定为逆时针顶点序列,所以只要两个相邻线段的叉乘都小于0则该多边形为凸多边形)

    /// <summary>
    /// 2D叉乘
    /// </summary>
    /// <param name="v1">点1</param>
    /// <param name="v2">点2</param>
    /// <returns></returns>
    public static float CrossProduct2D(Vector2 v1,Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }


    /// <summary>
    /// 点与线的位置关系
    /// </summary>
    /// <param name="point"></param>
    /// <param name="lineStart"></param>
    /// <param name="lineEnd"></param>
    /// <returns>==0:点在线上 <0:点在线的左侧 >0:点在线的右侧</returns>
    public static int IsPointToLinePosition(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float crossValue = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        if (crossValue < 0) return -1;
        if (crossValue > 0) return 1;
        return 0;
    }


    /// <summary>
    /// 是否为凸多边形
    /// </summary>
    /// <param name="points">逆时针点序列</param>
    /// <returns></returns>
    public static bool IsConvexPolygon(List<Vector2> points)
    {
        //计算每个顶点的转向,如果有不一致的转向,则表示该多边形不是凸多边形
        if (points.Count < 3) return false;
        bool isConvex = true;
        for (int i = 1; i < points.Count; i++)
        {
            Vector2 point = points[i];
            //上一个点
            Vector2 point1 = points[i - 1];
            //下一个点,如果超出当前点集合,则需要获取第一个点
            int nextIndex = i + 1;
            if (nextIndex >= points.Count) nextIndex = 0;
            Vector2 point2 = points[nextIndex];
            //计算朝向,因为点集合为逆时针点序列,如果点在线段右侧,则表示该角大于180 该多边形为凹多边形
            float value = IsPointToLinePosition(point1, point,point2);
            if (value > 0)
            {
                isConvex = false;
                break;
            }
        }
        return isConvex;
    }

8、判断线段与线段是否共线

思路:

1)先判断两条线段是否平行,即两条线段的叉积等于0
2)在判断两条线段是否共线,即线段1一个点在线段2的延长线上

	/// <summary>
    /// 线段与线段是否共线
    /// </summary>
    /// <param name="segment1Start"></param>
    /// <param name="segment1End"></param>
    /// <param name="segment2Start"></param>
    /// <param name="segment2End"></param>
    /// <returns></returns>
    public static bool IsSegmentCollineation(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
                    Vector2 segment2End)
    {
        //1.判断两个向量是否平行
        float value = CrossProduct2D(segment1End - segment1Start, segment2End - segment2Start);
        if (Mathf.Abs(value)<0.0003f)
        {    
            // 平行,则判断一个线上的点是否在另一线上
            if (IsPointOnLine(segment2Start, segment2End , segment2Start))
                return true;
        }
        return false;
    }

9、判断线段与线段是否重合(非相交)

1)判断两条线段必须共线
2)然后片段判断线段A的起终点是否在线段B,以及线段B的起终点,是否在线段A上,只要有一个条件成立,则可以认为两条线段是重合的

1)判断两条线段是否共线
2)对两条线段的4个定点进行排序,如果1,3 或 1,4 为为同一条线段上的点,则可以任务两条线段是否重合的

    /// <summary>
    /// 线段与线段是否重合(全部重合或局部重合)
    /// </summary>
    /// <param name="segment1Start"></param>
    /// <param name="segment1End"></param>
    /// <param name="segment2Start"></param>
    /// <param name="segment2End"></param>
    /// <returns></returns>
    public static bool IsSegmentCoincide(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
                    Vector2 segment2End)
    {
        //先判断两条线段是否在同一条线上
        if (!IsSegmentCollineation(segment1Start,segment1End,segment2Start,segment2End))
            return false;

        //如果是相同的起终点
        if (segment1Start == segment2Start && segment1End == segment2End) return true;
        //判断检测点是否在另一条线段上
        if (IsPointOnSegment2(segment1Start, segment2Start, segment2End) ||
            IsPointOnSegment2(segment1End, segment2Start, segment2End) ||
            IsPointOnSegment2(segment2Start, segment1Start, segment1End) ||
            IsPointOnSegment2(segment2End, segment1Start, segment1End))
            return true;
        return false;
    }

10、线段与线段是否相交

线段与线段是否相交1
线段与线段是否相交2

    /// <summary>
    /// 线段是否相交
    /// </summary>
    /// <param name="segment1Start"></param>
    /// <param name="segment1End"></param>
    /// <param name="segment2Start"></param>
    /// <param name="segment2End"></param>
    /// <returns></returns>
    public static bool IsSegmentIntersect(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
                    Vector2 segment2End)
    {
        //快速排斥实验
        if (Mathf.Min(segment1Start.x,segment1End.x) <= Mathf.Max(segment2Start.x,segment2End.x) 
            && Mathf.Min(segment2Start.x,segment2End.x) <= Mathf.Max(segment2Start.x,segment2End.x)
            && Mathf.Min(segment1Start.y,segment1End.y)<=Mathf.Max(segment2Start.y,segment2End.y)
            && Mathf.Min(segment2Start.y,segment2End.y) <= Mathf.Max(segment1Start.y,segment1End.y))
        {
            //先判断线段是否重合,重合,则认为也是相交
            if (IsSegmentCoincide(segment1Start, segment1End, segment2Start, segment2End))
                return true;
            //互为跨立的判断 一线的点相对另一线的位置关系,左右 -1 1,之和为 0
            int state = IsPointToLinePosition(segment1Start, segment2Start, segment2End) +
                        IsPointToLinePosition(segment1End, segment2Start, segment2End) +
                        IsPointToLinePosition(segment2Start, segment1Start, segment1End) +
                        IsPointToLinePosition(segment2End, segment1Start, segment1End);
            if (state==0) return true;
        }
        return false;
    }

11、计算直线与直线的交点

推导公式

    /// <summary>
    /// 求直线的交点
    /// </summary>
    /// <param name="line1Start"></param>
    /// <param name="line2End"></param>
    /// <param name="line2Start"></param>
    /// <param name="line2End"></param>
    /// <returns></returns>
    public static Vector2 LineIntersectPoint(Vector2 line1Start, Vector2 line1End, Vector2 line2Start,
                    Vector2 line2End)
    {
        //两点式公式
        //x0 = ((x3-x4) * (x2*y1 - x1*y2) - (x1-x2) * (x4*y3 - x3*y4)) / ((x3-x4) * (y1-y2) - (x1-x2) * (y3-y4));
        //y0 = ((y3-y4) * (y2*x1 - y1*x2) - (y1-y2) * (y4*x3 - y3*x4)) / ((y3-y4) * (x1-x2) - (y1-y2) * (x3-x4));
        
        float x1 = line1Start.x, x2 = line1End.x, x3 = line2Start.x, x4 = line2End.x;
        float y1 = line1Start.y, y2 = line1End.y, y3 = line2Start.y, y4 = line2End.y;
        
        Vector2 point = Vector2.zero;
        point.x = ((x3-x4) * (x2*y1 - x1*y2) - (x1-x2) * (x4*y3 - x3*y4)) / ((x3-x4) * (y1-y2) - (x1-x2) * (y3-y4));
        point.y = ((y3-y4) * (y2*x1 - y1*x2) - (y1-y2) * (y4*x3 - y3*x4)) / ((y3-y4) * (x1-x2) - (y1-y2) * (x3-x4));
        return point;
    }

12、线段与线段的交点

1)先判断线段与线段是否相交。
2)如果相交,则获取直线与直线的交点
其中,需要注意线段平行与重合情况。
平行:无交点
全部重合:返回其中一条线段的起点与终点
部分重合:返回重合部分的起点与终点

    /// <summary>
    /// 线段与线段的交点
    /// </summary>
    /// <param name="segment1Start"></param>
    /// <param name="segment1End"></param>
    /// <param name="segment2Start"></param>
    /// <param name="segment2End"></param>
    /// <param name="points"></param>
    /// <returns></returns>
    public static bool SegmentIntersectPoint(Vector2 segment1Start,Vector2 segment1End,Vector2 segment2Start,Vector2 segment2End,out List<Vector2> points)
    {
        //线段相同情况下 直接返回其中一条线段即可
        if (segment1Start==segment2Start && segment1End== segment2End)
        {
            points = new List<Vector2>(){segment1Start,segment1End};
            return true;
        }
        points = new List<Vector2>();
        //线段平行,且不共线,则无交点
        float value = CrossProduct2D(segment1End - segment1Start, segment2End - segment2Start);
        if (Mathf.Approximately(Mathf.Abs(value), 0))
        {
            //共线判断,一点是否在另一条线段上
            if (!IsPointOnLine(segment1Start,segment2Start,segment2End))
                return false;
            //平行且共线的情况下,需要知道线段是否重合
            if (IsPointOnSegment2(segment1Start, segment2Start, segment2End))
                points.Add(segment1Start);
            if (IsPointOnSegment2(segment1End, segment2Start, segment2End))
                points.Add(segment1End);
            if (IsPointOnSegment2(segment2Start, segment1Start, segment1End))
                points.Add(segment2Start);
            if (IsPointOnSegment2(segment2End, segment1Start, segment1End))
                points.Add(segment2End);
            if (points.Count <= 0) return false;
            return true;
        }

        //不平行,且不共线,则需要判断两个线段是否相交
        if (!IsSegmentIntersect(segment1Start, segment1End, segment2Start, segment2End))
            return false;
        
        //计算直线与直线的交点
        Vector2 outPoint = Vector2.zero;
        if (LineIntersectPoint(segment1Start,segment1End,segment2Start,segment2End,out outPoint))
        {
            points.Add(outPoint);
            return true;
        }
        
        /*********用此种方式也可*************************/
        //1.先求交点
        //2.在计算点是否在两线段上
        /*
        if (LineIntersectPoint(segment1Start,segment1End,segment2Start,segment2End,out outPoint))
        {
            if (IsPointOnLine(outPoint,segment1Start,segment1End) && IsPointOnLine(outPoint,segment2Start,segment2End))
            {
                points.Add(outPoint);
                return true;
            }
        }*/
        
        return false;
    }

13、射线与射线是否相交,以及交点

1)判断射线所在的直线是否相交
2)判断点是否在两条射线上

    /// <summary>
    /// 射线是否相交
    /// </summary>
    /// <param name="ray1Start"></param>
    /// <param name="ray1Dir"></param>
    /// <param name="ray2Start"></param>
    /// <param name="ray2Dir"></param>
    /// <returns></returns>
    public static bool IsRayIntersect(Vector2 ray1Start, Vector2 ray1Dir, Vector2 ray2Start, Vector2 ray2Dir,out Vector2 point)
    {
        //先计算两条直线是否相交
        if (!LineIntersectPoint(ray1Start,ray1Start+ray1Dir * 1,ray2Start,ray2Start+ray2Dir*1,out point))
            return false;
        if (Vector2.Dot((point - ray1Start).normalized, ray1Dir.normalized) < 0) return false;
        if (Vector2.Dot((point - ray2Start).normalized, ray2Dir.normalized) < 0) return false;
        return true;
    }

14、射线与线段是否相交,以及交点
1)所在直线是否相交
2)点是否在射线以及直线上

    /// <summary>
    /// 射线与线段是否相交
    /// </summary>
    /// <param name="rayStart"></param>
    /// <param name="rayDir"></param>
    /// <param name="segmentStart"></param>
    /// <param name="segmentEnd"></param>
    /// <returns></returns>
    public static bool IsRaySegmentIntersect(Vector2 rayStart,Vector2 rayDir,Vector2 segmentStart,Vector2 segmentEnd,out Vector2 point)
    {
        //先计算两条直线是否相交
        if (!LineIntersectPoint(rayStart,rayStart+rayDir * 1,segmentStart,segmentEnd,out point))
            return false;
        //判断交点的位置是否在射线上 方向相同可以确定点在射线上
        if (Vector2.Dot((point - rayStart).normalized, rayDir.normalized) < 0) return false;
        //点是否在线段上
        if (!IsPointOnSegment2(point,segmentStart, segmentEnd)) return false;
        return true;
    }

15.点围绕另一点旋转指定角度

2维向量旋转

需要注意的是,需要将角度转为弧度制进行计算(角度与弧度)

    /// <summary>
    /// 点绕另一个点进行旋转
    /// </summary>
    /// <param name="originPoint"></param>
    /// <param name="point"></param>
    /// <param name="angle"></param>
    /// <returns></returns>
    public static Vector2 PointRoationOnPoint(Vector2 originPoint, Vector2 point, float angle)
    {
        if (originPoint == point) return originPoint;
        Vector2 resultPoint = Vector2.zero;
        Vector2  v= (point - originPoint).normalized;
        angle *= Mathf.Deg2Rad;
        resultPoint.x = v.x * Mathf.Cos(angle) - v.y * Mathf.Sin(angle);
        resultPoint.y = v.x * Mathf.Sin(angle) + v.y * Mathf.Cos(angle);
        return resultPoint * Vector2.Distance(originPoint,point) + originPoint ;
    }
    
      /// <summary>
    /// 点集合绕点旋转
    /// </summary>
    /// <param name="points"></param>
    /// <param name="point"></param>
    /// <param name="angle"></param>
    /// <returns></returns>
    public static List<Vector2> PointsRoationOnPoint(List<Vector2> points, Vector2 point, float angle)
    {
        List<Vector2> resultPoints = new List<Vector2>();
        if (points == null) return resultPoints;
        for (int i = 0; i < points.Count; i++)
        {
            Vector2 nPoint = PointRoationOnPoint(point,points[i],angle);
            resultPoints.Add(nPoint);
        }
        return resultPoints;
    }

16、点是否在任意多变内

判断点是否在任意多变内部的几种方法
射线法:
1)求出多边形所在矩形,判断点是否在矩形内
2)点是否在多边形顶点或边上
3)以当前点做一条水平射线,计算该射线与多边形线段的相交数量,如果为奇数则点在多边形内部

    /// <summary>
    /// 点是否在任意多边形内部
    /// </summary>
    /// <param name="point"></param>
    /// <param name="polygon"></param>
    /// <returns>-1:不在多边形内 0:在多边形上 1:多边形内 </returns>
    public static int IsPointInAnyPolygon(Vector2 point, List<Vector2> polygon)
    {
        //顶点数量小于3,则无法形成多边形
        if (polygon.Count < 3) return -1;

        //1.先获取多边形所在矩形范围内

        Vector2 rectMin = polygon[0], rectMax = polygon[0];
        for (int i = 1; i < polygon.Count; i++)
        {
            if (polygon[i].x < rectMin.x) rectMin.x = polygon[i].x;
            if (polygon[i].y < rectMin.y) rectMin.y = polygon[i].y;
            if (polygon[i].x > rectMax.x) rectMax.x = polygon[i].x;
            if (polygon[i].y > rectMax.y) rectMax.y = polygon[i].y;
        }
        if (point.x < rectMin.x || point.y < rectMin.y || point.x > rectMax.x || point.y > rectMax.y) return -1;
        //2.射线相交点计算
        int intersectCount = 0;
        for (int i = 0; i < polygon.Count; i++)
        {
            int nextIndex = i + 1;
            if (nextIndex >= polygon.Count)
                nextIndex = 0;
            //目标在顶点上
            if (polygon[i] == point || polygon[nextIndex] == point)
                return 0;
            //目标在线段上
            if (IsPointOnSegment2(point, polygon[i], polygon[nextIndex]))
                return 0;
            Vector2 intersectPoint;
            //射线与线段相交
            if (IsRaySegmentIntersect(point,Vector2.right,polygon[i],polygon[nextIndex],out intersectPoint))
            {
                //如果相交为线段的顶点,则需要增加2,因为在同一点进入,又在同一个点出去
                if (intersectPoint == polygon[i] || intersectPoint == polygon[nextIndex])
                    intersectCount += 2;
                else
                    intersectCount += 1;
            }
        }
        return intersectCount % 2 == 1 ? 1:-1;
    }

17、点是否在椭圆内

c# 实现求一个点是否在一个面内

如果P(x,y)是否在椭圆内,则小于等于1

焦点在x轴上时,长轴为2a,短轴为2b,圆方程为x^2/a^2+y^2/b^2=1
焦点在y轴上时,长轴为2a,短轴为2b,圆方程为x^2/b^2+y^2/a^2=1
笔误

    /// <summary>
    /// 点是否在椭圆内
    /// </summary>
    /// <param name="point">目标点</param>
    /// <param name="xAxis">长轴半径</param>
    /// <param name="yAxis">短轴半径</param>
    /// <returns></returns>
    private bool PointIsInEllipse(Vector2 point,float xAxis,float yAxis)
    {
        return (point.x * point.x) / (xAxis  * xAxis ) + (point.y * point.y) / (yAxis  * yAxis )<=1;
    }

18、直线与椭圆的交点计算

直线与椭圆相交求交点

  /// <summary>
        /// 线段和椭圆弧的交点
        /// </summary>
        /// <param name="line">线段对象</param>
        /// <param name="ht">存储交点和error信息</param>
        /// <returns>返回交点和error信息</returns>
        internal static Hashtable LineIntersectEllipticArc(Line line, Hashtable ht)
        {
            //线段的终点
            Vector2 LineendPoint = line.endPoint;
            //线段的起点
            Vector2 LinestartPoint = line.startPoint;
            //椭圆弧的长轴的一半
            double maxAxis = 262.39051820125013 / 2;
            //椭圆弧的短轴一半
            double minAxis = 135.76528231694766 / 2;
            //椭圆弧的起点
            //椭圆弧的起始角度 
            double startAngle = DegreeToRadian(132.0438345714015);
            //椭圆弧的终点
            //椭圆弧的终止角度 //默认设置为90度对应的弧度
            double endAngle = DegreeToRadian(258.13766538237763);
            //椭圆弧的圆心
            Vector2 center = new Vector2(17.7894639270817, 15.0254309579905);
            //设置原点坐标(0,0)
            Vector2 centerZero = new Vector2(0, 0);
            //椭圆弧的导入角度'
            double angle = DegreeToRadian(191.75357833501226);
            //将直线顺时针旋转angle度
            Vector2[] ve = Anticlockwise(LineendPoint, LinestartPoint, center, angle);
            //假设默认交点坐标
            Vector2 ptInter1 = new Vector2(0, 0);
            Vector2 ptInter2 = new Vector2(0, 0);
            //直线和椭圆的交点坐标
            LineIntersectEllipse(MoveOne(ve[0], center), MoveOne(ve[1], center), maxAxis, minAxis, ref ptInter1, ref ptInter2);
            //用于存储交点
            List<Vector2> node = new List<Vector2>();
            //用于判断是否有交点
            bool judgeNode = false;
            //存储的版本号
            int version = 0;
            double a = NormalizeRadianAngle(startAngle);
            double b = NormalizeRadianAngle(endAngle);
            if (Zero(ptInter1))
            {
                JudgeDotQualified(centerZero, ptInter1, a, b, node);
                if (node.Count == 1)
                {
                    if (node[0] != null)
                    {
                        //表示第一次有一个值
                        //将交点逆时针旋转
                        Vector2[] ve1 = Anticlockwise(ptInter1, ptInter2, centerZero, angle, false);
                        //再将交点平移得到最后真正的交点
                        ve1[0] = MoveTwo(ve1[0], center);
                        node[0] = ve1[0];
                        version = 1;
                    }
                }
            }
            if (Zero(ptInter2))
            {
                if (version == 0)
                {
                    JudgeDotQualified(centerZero, ptInter2, a, b, node);
                    if (node.Count == 1)
                    {
                        if (node[0] != null)
                        {
                            //表示第一次没有一个值
                            //将交点逆时针旋转
                            Vector2[] ve1 = Anticlockwise(ptInter1, ptInter2, centerZero, angle, false);
                            //再将交点平移得到最后真正的交点
                            ve1[1] = MoveTwo(ve1[1], center);
                            node[0] = ve1[1];
                        }
                    }
                }
                else if (version == 1)
                {
                    JudgeDotQualified(centerZero, ptInter2, a, b, node);
                    if (node.Count == 2)
                    {
                        if (node[1] != null)
                        {
                            //表示第一次有一个值
                            //将交点逆时针旋转
                            Vector2[] ve1 = Anticlockwise(ptInter1, ptInter2, centerZero, angle, false);
                            //再将交点平移得到最后真正的交点
                            ve1[1] = MoveTwo(ve1[1], center);
                            node[1] = ve1[1];
                        }
                    }
                }
            }
            ht.Add("node", node);
            return ht;
        }
   /// <summary>
        /// 角度转弧度
        /// </summary>
        /// <param name="angle">角度</param>
        /// <returns>弧度</returns>
        public static double DegreeToRadian(double angle)
        {
            return ((angle * System.Math.PI) / 180.0);
        }
        public const double EPSILON = 1E-12;
 public static bool IsEqual(double x, double y, double epsilon = EPSILON)
        {
            return IsEqualZero(x - y, epsilon);
        }
        public static bool IsEqualZero(double x, double epsilon = EPSILON)
        {
            return (System.Math.Abs(x) < epsilon);
        }
        /// <summary>
        /// 点的绕椭圆弧弧心旋转
        /// </summary>
        /// <param name="LineendPoint">线段终点</param>
        /// <param name="LinestartPoint">线段起点</param>
        /// <param name="center">椭圆弧弧心</param>
        /// <param name="angle">椭圆弧的导入角度'</param>
        /// /// <param name="isClockwise">顺逆旋转,默认为顺时针旋转</param>
        internal static Vector2[] Anticlockwise(Vector2 LineendPoint, Vector2 LinestartPoint, Vector2 center, double angle, bool isClockwise = true)
        {
            Vector2[] ve = new Vector2[2];
            if (isClockwise)
            {
                angle = -angle;
            }
            double cos = System.Math.Cos(angle);
            double sin = System.Math.Sin(angle);
            if (IsEqual(cos, 0))
            {
                cos = 0;
            }
            if (IsEqual(sin, 0))
            {
                sin = 0;
            }
            double x = ((LineendPoint.x - center.x) * cos - (LineendPoint.y - center.y) * sin + center.x);
            double y = ((LineendPoint.x - center.x) * sin + (LineendPoint.y - center.y) * cos + center.y);
            ve[0].x = x;
            ve[0].y = y;
            double x1 = ((LinestartPoint.x - center.x) * cos - (LinestartPoint.y - center.y) * sin + center.x);
            double y1 = ((LinestartPoint.x - center.x) * sin + (LinestartPoint.y - center.y) * cos + center.y);
            ve[1].x = x1;
            ve[1].y = y1;
            return ve;
        }
        /// <summary>
        /// 第一次平移
        /// </summary>
        /// <param name="dot">交点坐标</param>
        /// <param name="center">圆心坐标</param>
        /// <returns>返回平移后的点坐标</returns>
        internal static Vector2 MoveOne(Vector2 dot, Vector2 center)
        {
            Vector2 returnDot = new Vector2();
            if (center.x >= 0 && center.y >= 0)
            {
                //圆心在第一象限
                returnDot.x = dot.x - center.x;
                returnDot.y = dot.y - center.y;
            }
            else if (center.x <= 0 && center.y >= 0)
            {
                //圆心在第二象限
                returnDot.x = dot.x + center.x;
                returnDot.y = dot.y - center.y;
            }
            else if (center.x <= 0 && center.y <= 0)
            {
                //圆心在第三象限
                returnDot.x = dot.x + center.x;
                returnDot.y = dot.y + center.y;
            }
            else if (center.x >= 0 && center.y <= 0)
            {  //圆心在第四象限
                returnDot.x = dot.x - center.x;
                returnDot.y = dot.y + center.y;
            }
            return returnDot;
        }
        /// <summary>
        /// 第二次平移
        /// </summary>
        /// <param name="dot">点坐标</param>
        /// <param name="center">圆心坐标</param>
        /// <returns>返回平移后的点</returns>
        internal static Vector2 MoveTwo(Vector2 dot, Vector2 center)
        {
            Vector2 returnDot = new Vector2();
            if (center.x >= 0 && center.y >= 0)
            {
                //圆心在第一象限
                returnDot.x = dot.x + center.x;
                returnDot.y = dot.y + center.y;
            }
            else if (center.x <= 0 && center.y >= 0)
            {
                //圆心在第二象限
                returnDot.x = dot.x - center.x;
                returnDot.y = dot.y + center.y;
            }
            else if (center.x <= 0 && center.y <= 0)
            {
                //圆心在第三象限
                returnDot.x = dot.x - center.x;
                returnDot.y = dot.y - center.y;
            }
            else if (center.x >= 0 && center.y <= 0)
            {  //圆心在第四象限
                returnDot.x = dot.x + center.x;
                returnDot.y = dot.y - center.y;
            }
            return returnDot;
        }
        /// <summary>
        /// 判断点是否为(0,0)
        /// </summary>
        /// <param name="dot">交点</param>
        /// <returns>是(0,0)返回false</returns>
        internal static bool Zero(Vector2 dot)
        {
            if (dot.x == 0)
            {
                if (dot.y == 0)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return true;
            }
        }
        /// <summary>
        /// 规整化弧度
        /// 返回值范围:[0, 2*PI)
        /// </summary>
        internal static double NormalizeRadianAngle(double rad)
        {
            double value = rad % (2 * System.Math.PI);
            if (value < 0)
                value += 2 * System.Math.PI;
            return value;
        }

猜你喜欢

转载自blog.csdn.net/u014361280/article/details/128148129