Circle line-segment collision detection algorithm?

前端 未结 28 1716
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 06:38

I have a line from A to B and a circle positioned at C with the radius R.

\"Image\"

What is a good alg

28条回答
  •  攒了一身酷
    2020-11-22 07:06

    Another one in c# (partial Circle class). Tested and works like a charm.

    public class Circle : IEquatable
    {
        // ******************************************************************
        // The center of a circle
        private Point _center;
        // The radius of a circle
        private double _radius;
    
       // ******************************************************************
        /// 
        /// Find all intersections (0, 1, 2) of the circle with a line defined by its 2 points.
        /// Using: http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle
        /// Note: p is the Center.X and q is Center.Y
        /// 
        /// 
        /// 
        /// 
        public List GetIntersections(Point linePoint1, Point linePoint2)
        {
            List intersections = new List();
    
            double dx = linePoint2.X - linePoint1.X;
    
            if (dx.AboutEquals(0)) // Straight vertical line
            {
                if (linePoint1.X.AboutEquals(Center.X - Radius) || linePoint1.X.AboutEquals(Center.X + Radius))
                {
                    Point pt = new Point(linePoint1.X, Center.Y);
                    intersections.Add(pt);
                }
                else if (linePoint1.X > Center.X - Radius && linePoint1.X < Center.X + Radius)
                {
                    double x = linePoint1.X - Center.X;
    
                    Point pt = new Point(linePoint1.X, Center.Y + Math.Sqrt(Radius * Radius - (x * x)));
                    intersections.Add(pt);
    
                    pt = new Point(linePoint1.X, Center.Y - Math.Sqrt(Radius * Radius - (x * x)));
                    intersections.Add(pt);
                }
    
                return intersections;
            }
    
            // Line function (y = mx + b)
            double dy = linePoint2.Y - linePoint1.Y;
            double m = dy / dx;
            double b = linePoint1.Y - m * linePoint1.X;
    
            double A = m * m + 1;
            double B = 2 * (m * b - m * _center.Y - Center.X);
            double C = Center.X * Center.X + Center.Y * Center.Y - Radius * Radius - 2 * b * Center.Y + b * b;
    
            double discriminant = B * B - 4 * A * C;
    
            if (discriminant < 0)
            {
                return intersections; // there is no intersections
            }
    
            if (discriminant.AboutEquals(0)) // Tangeante (touch on 1 point only)
            {
                double x = -B / (2 * A);
                double y = m * x + b;
    
                intersections.Add(new Point(x, y));
            }
            else // Secant (touch on 2 points)
            {
                double x = (-B + Math.Sqrt(discriminant)) / (2 * A);
                double y = m * x + b;
                intersections.Add(new Point(x, y));
    
                x = (-B - Math.Sqrt(discriminant)) / (2 * A);
                y = m * x + b;
                intersections.Add(new Point(x, y));
            }
    
            return intersections;
        }
    
        // ******************************************************************
        // Get the center
        [XmlElement("Center")]
        public Point Center
        {
            get { return _center; }
            set
            {
                _center = value;
            }
        }
    
        // ******************************************************************
        // Get the radius
        [XmlElement]
        public double Radius
        {
            get { return _radius; }
            set { _radius = value; }
        }
    
        //// ******************************************************************
        //[XmlArrayItemAttribute("DoublePoint")]
        //public List Coordinates
        //{
        //    get { return _coordinates; }
        //}
    
        // ******************************************************************
        // Construct a circle without any specification
        public Circle()
        {
            _center.X = 0;
            _center.Y = 0;
            _radius = 0;
        }
    
        // ******************************************************************
        // Construct a circle without any specification
        public Circle(double radius)
        {
            _center.X = 0;
            _center.Y = 0;
            _radius = radius;
        }
    
        // ******************************************************************
        // Construct a circle with the specified circle
        public Circle(Circle circle)
        {
            _center = circle._center;
            _radius = circle._radius;
        }
    
        // ******************************************************************
        // Construct a circle with the specified center and radius
        public Circle(Point center, double radius)
        {
            _center = center;
            _radius = radius;
        }
    
        // ******************************************************************
        // Construct a circle based on one point
        public Circle(Point center)
        {
            _center = center;
            _radius = 0;
        }
    
        // ******************************************************************
        // Construct a circle based on two points
        public Circle(Point p1, Point p2)
        {
            Circle2Points(p1, p2);
        }
    

    Required:

    using System;
    
    namespace Mathematic
    {
        public static class DoubleExtension
        {
            // ******************************************************************
            // Base on Hans Passant Answer on:
            // http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
    
            /// 
            /// Compare two double taking in account the double precision potential error.
            /// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
            public static bool AboutEquals(this double value1, double value2)
            {
                if (double.IsPositiveInfinity(value1))
                    return double.IsPositiveInfinity(value2);
    
                if (double.IsNegativeInfinity(value1))
                    return double.IsNegativeInfinity(value2);
    
                if (double.IsNaN(value1))
                    return double.IsNaN(value2);
    
                double epsilon = Math.Max(Math.Abs(value1), Math.Abs(value2)) * 1E-15;
                return Math.Abs(value1 - value2) <= epsilon;
            }
    
            // ******************************************************************
            // Base on Hans Passant Answer on:
            // http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
    
            /// 
            /// Compare two double taking in account the double precision potential error.
            /// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
            /// You get really better performance when you can determine the contextual epsilon first.
            /// 
            /// 
            /// 
            /// 
            /// 
            public static bool AboutEquals(this double value1, double value2, double precalculatedContextualEpsilon)
            {
                if (double.IsPositiveInfinity(value1))
                    return double.IsPositiveInfinity(value2);
    
                if (double.IsNegativeInfinity(value1))
                    return double.IsNegativeInfinity(value2);
    
                if (double.IsNaN(value1))
                    return double.IsNaN(value2);
    
                return Math.Abs(value1 - value2) <= precalculatedContextualEpsilon;
            }
    
            // ******************************************************************
            public static double GetContextualEpsilon(this double biggestPossibleContextualValue)
            {
                return biggestPossibleContextualValue * 1E-15;
            }
    
            // ******************************************************************
            /// 
            /// Mathlab equivalent
            /// 
            /// 
            /// 
            /// 
            public static double Mod(this double dividend, double divisor)
            {
                return dividend - System.Math.Floor(dividend / divisor) * divisor;
            }
    
            // ******************************************************************
        }
    }
    

提交回复
热议问题