Task: Line circle intersection



C#: using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        Circle circle = ((3, -5), 3);
        Line[] lines = {
            ((-10, 11), (10, -9)),
            ((-10, 11), (-11, 12), true),
            ((3, -2), (7, -2))
        };
        Print(circle, lines);
        
        circle = ((0, 0), 4);
        lines = new Line[] {
            ((0, -3), (0, 6)),
            ((0, -3), (0, 6), true)
        };
        Print(circle, lines);
        
        circle = ((4, 2), 5);
        lines = new Line[] {
            ((6, 3), (10, 7)),
            ((7, 4), (11, 8), true)
        };
        Print(circle, lines);
    }
    
    static void Print(Circle circle, Line[] lines)
    {
        Console.WriteLine($"Circle: {circle}");
        foreach (var line in lines) {
            Console.WriteLine($"\t{(line.IsSegment ? "Segment:" : "Line:")} {line}");
            var points = Intersection(circle, line).ToList();
            Console.WriteLine(points.Count == 0 ? "\t\tdo not intersect" : "\t\tintersect at " + string.Join(" and ", points));
        }
        Console.WriteLine();
    }
    
    static IEnumerable<Point> Intersection(Circle circle, Line line)
    {
        var intersection = LineIntersection(circle, line);
        return line.IsSegment
            ? intersection.Where(p => p.CompareTo(line.P1) >= 0 && p.CompareTo(line.P2) <= 0)
            : intersection;

        static IEnumerable<Point> LineIntersection(Circle circle, Line line)
        {
            double x, y, A, B, C, D;
            var (m, c) = (line.Slope, line.YIntercept);
            var (p, q, r) = (circle.X, circle.Y, circle.Radius);

            if (line.IsVertical) {
                x = line.P1.X;
                B = -2 * q;
                C = p * p + q * q - r * r + x * x - 2 * p * x;
                D = B * B - 4 * C;
                if (D == 0) yield return (x, -q);
                else if (D > 0) {
                    D = Math.Sqrt(D);
                    yield return (x, (-B - D) / 2);
                    yield return (x, (-B + D) / 2);
                }
            } else {
                A = m * m + 1;
                B = 2 * (m * c - m * q - p);
                C = p * p + q * q - r * r + c * c - 2 * c * q;
                D = B * B - 4 * A * C;
                if (D == 0) {
                    x = -B / (2 * A);
                    y = m * x + c;
                    yield return (x, y);
                } else if (D > 0) {
                    D = Math.Sqrt(D);
                    x = (-B - D) / (2 * A);
                    y = m * x + c;
                    yield return (x, y);
                    x = (-B + D) / (2 * A);
                    y = m * x + c;
                    yield return (x, y);
                }
            }
        }

    }
    
    readonly struct Point : IComparable<Point>
    {
        public Point(double x, double y) => (X, Y) = (x, y);
        
        public static implicit operator Point((double x, double y) p) => new Point(p.x, p.y);
        
        public double X { get; }
        public double Y { get; }
        
        public int CompareTo(Point other)
        {
            int c = X.CompareTo(other.X);
            if (c != 0) return c;
            return Y.CompareTo(other.Y);
        }
        
        public override string ToString() => $"({X}, {Y})";
    }
    
    readonly struct Line
    {
        public Line(Point p1, Point p2, bool isSegment = false)
        {
            (P1, P2) = p2.CompareTo(p1) < 0 ? (p2, p1) : (p1, p2);
            IsSegment = isSegment;
            if (p1.X == p2.X) (Slope, YIntercept) = (double.PositiveInfinity, double.NaN);
            else {
                Slope = (P2.Y - P1.Y) / (P2.X - P1.X);
                YIntercept = P2.Y - Slope * P2.X;
            }
        }
        
        public static implicit operator Line((Point p1, Point p2) l) => new Line(l.p1, l.p2);
        public static implicit operator Line((Point p1, Point p2, bool isSegment) l) => new Line(l.p1, l.p2, l.isSegment);
        
        public Point P1 { get; }
        public Point P2 { get; }
        public double Slope { get; }
        public double YIntercept { get; }
        public bool IsSegment { get; }
        public bool IsVertical => P1.X == P2.X;
        
        public override string ToString() => $"[{P1}, {P2}]";
    }
    
    readonly struct Circle
    {
        public Circle(Point center, double radius) => (Center, Radius) = (center, radius);
        
        public static implicit operator Circle((Point center, double radius) c) => new Circle(c.center, c.radius);
        
        public Point Center { get; }
        public double Radius { get; }
        public double X => Center.X;
        public double Y => Center.Y;
        
        public override string ToString() => $"{{ C:{Center}, R:{Radius} }}";
    }   
}






VB: Module Module1

    Structure Point
        Implements IComparable(Of Point)

        Public Sub New(mx As Double, my As Double)
            X = mx
            Y = my
        End Sub

        Public ReadOnly Property X As Double
        Public ReadOnly Property Y As Double

        Public Function CompareTo(other As Point) As Integer Implements IComparable(Of Point).CompareTo
            Dim c = X.CompareTo(other.X)
            If c <> 0 Then
                Return c
            End If
            Return Y.CompareTo(other.Y)
        End Function

        Public Overrides Function ToString() As String
            Return String.Format("({0}, {1})", X, Y)
        End Function
    End Structure

    Structure Line
        Public Sub New(mp1 As Point, mp2 As Point, Optional segment As Boolean = False)
            If P2.CompareTo(P1) < 0 Then
                P1 = mp2
                P2 = mp1
            Else
                P1 = mp1
                P2 = mp2
            End If
            IsSegment = segment
            If P1.X = P2.X Then
                Slope = Double.PositiveInfinity
                YIntercept = Double.NaN
            Else
                Slope = (P2.Y - P1.Y) / (P2.X - P1.X)
                YIntercept = P2.Y - Slope * P2.X
            End If
        End Sub

        Public ReadOnly Property P1 As Point
        Public ReadOnly Property P2 As Point
        Public ReadOnly Property Slope As Double
        Public ReadOnly Property YIntercept As Double
        Public ReadOnly Property IsSegment As Boolean

        Public Function IsVertical() As Boolean
            Return P1.X = P2.X
        End Function

        Public Overrides Function ToString() As String
            Return String.Format("[{0}, {1}]", P1, P2)
        End Function
    End Structure

    Structure Circle
        Public Sub New(c As Point, r As Double)
            Center = c
            Radius = r
        End Sub

        Public ReadOnly Property Center As Point
        Public ReadOnly Property Radius As Double

        Public Function X() As Double
            Return Center.X
        End Function

        Public Function Y() As Double
            Return Center.Y
        End Function

        Public Overrides Function ToString() As String
            Return String.Format("{{ C:{0}, R:{1} }}", Center, Radius)
        End Function
    End Structure

    Function Intersection(oc As Circle, ol As Line) As IEnumerable(Of Point)
        Dim LineIntersection = Iterator Function(ic As Circle, il As Line) As IEnumerable(Of Point)
                                   Dim m = il.Slope
                                   Dim c = il.YIntercept
                                   Dim p = ic.X
                                   Dim q = ic.Y
                                   Dim r = ic.Radius

                                   If il.IsVertical Then
                                       Dim x = il.P1.X
                                       Dim B = -2 * q
                                       Dim CC = p * p + q * q - r * r + x * x - 2 * p * x
                                       Dim D = B * B - 4 * CC
                                       If D = 0 Then
                                           Yield New Point(x, -q)
                                       ElseIf D > 0 Then
                                           D = Math.Sqrt(D)
                                           Yield New Point(x, (-B - D) / 2)
                                           Yield New Point(x, (-B + D) / 2)
                                       End If
                                   Else
                                       Dim A = m * m + 1
                                       Dim B = 2 * (m * c - m * q - p)
                                       Dim CC = p * p + q * q - r * r + c * c - 2 * c * q
                                       Dim D = B * B - 4 * A * CC
                                       If D = 0 Then
                                           Dim x = -B / (2 * A)
                                           Dim y = m * x + c
                                           Yield New Point(x, y)
                                       ElseIf D > 0 Then
                                           D = Math.Sqrt(D)
                                           Dim x = (-B - D) / (2 * A)
                                           Dim y = m * x + c
                                           Yield New Point(x, y)
                                           x = (-B + D) / (2 * A)
                                           y = m * x + c
                                           Yield New Point(x, y)
                                       End If
                                   End If
                               End Function

        Dim int = LineIntersection(oc, ol)
        If ol.IsSegment Then
            Return int.Where(Function(p) p.CompareTo(ol.P1) >= 0 AndAlso p.CompareTo(ol.P2) <= 0)
        Else
            Return int
        End If
    End Function

    Sub Print(c As Circle, lines() As Line)
        Console.WriteLine("Circle: {0}", c)
        For Each line In lines
            Console.Write(vbTab)
            If line.IsSegment Then
                Console.Write("Segment: ")
            Else
                Console.Write("Line: ")
            End If
            Console.WriteLine(line)

            Dim points = Intersection(c, line).ToList

            Console.Write(vbTab + vbTab)
            If points.Count = 0 Then
                Console.WriteLine("do not intersect")
            Else
                Console.WriteLine("intersect at {0}", String.Join(" and ", points))
            End If
        Next
        Console.WriteLine()
    End Sub

    Sub Main()
        Dim c = New Circle(New Point(3, -5), 3)
        Dim lines() As Line = {
            New Line(New Point(-10, 11), New Point(10, -9)),
            New Line(New Point(-10, 11), New Point(-11, 12), True),
            New Line(New Point(3, -2), New Point(7, -2))
            }
        Print(c, lines)

        c = New Circle(New Point(0, 0), 4)
        lines = {
            New Line(New Point(0, -3), New Point(0, 6)),
            New Line(New Point(0, -3), New Point(0, 6), True)
            }
        Print(c, lines)

        c = New Circle(New Point(4, 2), 5)
        lines = {
            New Line(New Point(6, 3), New Point(10, 7)),
            New Line(New Point(7, 4), New Point(11, 8), True)
            }
        Print(c, lines)
    End Sub

End Module