欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

c# 直线和椭圆弧的交点坐标算法

程序员文章站 2022-03-31 11:41:03
...

在求直线和椭圆弧的交点坐标的时候,我们同样和我上一个求直线和圆弧交点坐标一样,先来分析一下,主要分为
第一步: 求直线和椭圆弧交点,我们可以先求直线和椭圆弧的交点。
第二步:由于椭圆的可能是有角度的,椭圆圆心不在坐标轴的原点上,比如长轴不在X轴上,也不在Y轴上,这样的椭圆就是有角度的椭圆,这样的椭圆我们不好进行计算。对此,我们需要将圆弧旋转,然后再平移,使长轴在X轴上,是椭圆圆心在原点上,但是你会发现这样操作椭圆还是比较麻烦,因此,我们可以操作直线,想象椭圆圆心已经移动到了原点,因为移动椭圆只需要移动椭圆圆心就好,这样我们也根据移动椭圆圆心一样的步伐移动直线,然后求直线和假设移动后的椭圆的交点,然后根据椭圆圆心一样的步伐反向移动回去,得到的点就是直线和有角度的椭圆交点。(有点绕,可以画图理解哦)
第三步:最终通过判断交点是否在椭圆弧上。

下面开始展示代码,伸手党有福了,可能有些地方写的不好,望谅解,创作不易,喜欢给个好评吧。

主干

  /// <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;
        }

计算线段和椭圆的

 /// <summary>
        /// 计算线段和椭圆的交点
        /// </summary>
        /// <param name="LineendPoint">线段的终点</param>
        /// <param name="LinestartPoint">线段的起始点</param>
        /// <param name="maxAxis">长轴的长度的一半</param>
        /// <param name="minAxis">短轴的长度的一半</param>
        /// <param name="angle">椭圆弧的导入角度'</param>
        /// <param name="ptInter1">交点一</param>
        /// <param name="ptInter2">交点二</param>
        /// <returns>返回是否有交点</returns>
        internal static bool LineIntersectEllipse(Vector2 LineendPoint, Vector2 LinestartPoint, double maxAxis, double minAxis, ref Vector2 ptInter1, ref Vector2 ptInter2)
        {
            //直线方程为y=kx+b 求出k,c
            double k, c;
            k = (LineendPoint.y - LinestartPoint.y) / (LineendPoint.x - LinestartPoint.x);
            c = LinestartPoint.y - k * LinestartPoint.x;
            //maxAxis为x轴上的长轴 ,minAxis为y轴上的短轴
            //判断是否有交点
            double media = ((2 * maxAxis * maxAxis * k * c) * (2 * maxAxis * maxAxis * k * c) - 4 * (minAxis * minAxis + k * k * maxAxis * maxAxis) * maxAxis * maxAxis * (c * c - minAxis * minAxis));
            if (media > 0)
            {
                //直线和椭圆有两个交点
                ptInter1.x = (-2 * maxAxis * maxAxis * k * c + System.Math.Sqrt(media)) / (2 * (minAxis * minAxis + k * k * maxAxis * maxAxis));
                ptInter1.y = k * ptInter1.x + c;
                ptInter2.x = (-2 * maxAxis * maxAxis * k * c - System.Math.Sqrt(media)) / (2 * (minAxis * minAxis + k * k * maxAxis * maxAxis));
                ptInter2.y = k * ptInter2.x + c;
                return true;
            }
            else if (media == 0)
            {
                //直线和椭圆只有一个交点
                ptInter1.x = (-2 * maxAxis * maxAxis * k * c + System.Math.Sqrt(media)) / (2 * (minAxis * minAxis + k * k * maxAxis * maxAxis));
                ptInter1.y = k * ptInter1.x + c;
                return true;
            }
            else
            {
                //直线和椭圆没有交点
                return false;
            }
        }

判断交点是否合格方法,也就是交点是否在椭圆弧上


        /// <summary>
        /// 判断交点是否合格方法
        /// </summary>
        /// <param name="vector">中心圆心</param>
        /// <param name="ptInter1">交点</param>
        /// <param name="startAngle">圆弧起始角度</param>
        /// <param name="endAngle">圆弧终止角度</param>
        /// <param name="node">存放交点集合</param>
        /// <param name="_direction">圆弧得顺逆,默认为逆时针</param>
        internal static void JudgeDotQualified(Vector2 vector, Vector2 ptInter1, double startAngle, double endAngle, List<Vector2> node, bool _direction = false)
        {
            if (!_direction)
            {
                //画弧为逆时针
                //得到一个交点对应的弧度
                double radian = Tangent(vector, ptInter1);
                if (startAngle > endAngle)
                { //起始角度大于终止角度时,交点角度小于终止角度或者交点角度大于起始角度就会与圆弧相交
                    if (radian <= endAngle || radian >= startAngle)
                    {
                        node.Add(ptInter1);
                    }
                }
                else
                { //起始角度小于终止角度时,交点角度在起始角度和终止角度之间就会与圆弧相交
                    if (startAngle < radian && radian < endAngle)
                    {
                        node.Add(ptInter1);
                    }
                    else if (startAngle == radian || endAngle == radian)
                    {
                        node.Add(ptInter1);
                    }
                }
            }
            else
            {
                //画弧为顺时针
                //得到一个交点对应的弧度
                double radian = MathUtils.Tangent(vector, ptInter1);
                if (startAngle < endAngle)
                { //起始角度小于终止角度时,交点角度大于终止角度或者交点角度小于起始角度就会与圆弧相交
                    if (radian <= startAngle || radian >= endAngle)
                    {
                        node.Add(ptInter1);
                    }
                }
                else
                { //起始角度大于终止角度时,交点角度在终止角度和起始角度之间就会与圆弧相交
                    if (endAngle < radian && radian < startAngle)
                    {
                        node.Add(ptInter1);
                    }
                    else if (startAngle == radian || endAngle == radian)
                    {
                        node.Add(ptInter1);
                    }
                }
            }
        }
        /// <summary>
        /// 利用正切值
        /// 计算交点对应的弧度
        /// </summary>
        /// <param name="vector">圆心坐标</param>
        /// <param name="intersect">交点坐标</param>
        /// <returns>弧度</returns>
        internal static double Tangent(Vector2 vector, Vector2 intersect)
        {
            //圆心的x ,y 坐标
            double vectorX = vector.x;
            double vectorY = vector.y;
            //交点的  x ,y 坐标
            double intersectX = intersect.x;
            double intersectY = intersect.y;
            //两点的x轴的差值
            double difX = intersectX - vectorX;
            //两点的y轴的差值
            double difY = intersectY - vectorY;
            if (intersectY == vectorY && intersectX > 0)
            {
                //交点在正x轴上,角度为0,弧度为零
                return 0;
            }
            else if (intersectX == vectorX && intersectY > 0)
            {
                //交点在正y轴上,角度为90度
                return DegreeToRadian(90);
            }
            else if (intersectY == vectorY && intersectX < 0)
            {
                //交点在负x轴上,角度为180度
                return DegreeToRadian(180);
            }
            else if (intersectX == vectorX && intersectY < 0)
            {
                //交点在负数y轴上,角度为270度
                return DegreeToRadian(270);
            }
            else
            {
                if (difX > 0 && difY > 0)
                {
                    //锐角
                    double number = difY / difX;
                    return TangentToDegree(number);
                }
                else if (difX < 0 && difY > 0)
                {
                    //小于180度的钝角
                    double number = difY / (-difX);
                    return DegreeToRadian(180) - TangentToDegree(number);
                }
                else if (difX < 0 && difY < 0)
                {
                    //大于180度小于270度的钝角
                    double number = (-difY) / (-difX);
                    return TangentToDegree(number) + DegreeToRadian(180);
                }
                else
                {
                    //(difX > 0 && difY < 0)
                    //大于270度小于360度的钝角
                    double number = (-difY) / difX;
                    return DegreeToRadian(360) - TangentToDegree(number);
                }
            }
        }
        /// <summary>
        /// 正切值转弧度
        /// </summary>
        /// <returns>返回一个弧度</returns>
        public static double TangentToDegree(double tangent)
        {
            //得到正切值对应的弧度
            return System.Math.Atan(tangent);
        }

总结: 由上面的分析可以看出,在分析第二步的时候,有点绕,但是你可以画图理解,再结合代码学习,会发现也不是很难,当然最后不要忘记,点赞,好评加关注哦。