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



Java: import java.util.*;
import java.awt.geom.*;

public class LineCircleIntersection {
    public static void main(String[] args) {
        try {
            demo();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void demo() throws NoninvertibleTransformException {
        Point2D center = makePoint(3, -5);
        double radius = 3.0;
        System.out.println("The intersection points (if any) between:");
        System.out.println("\n  A circle, center (3, -5) with radius 3, and:");
        System.out.println("\n    a line containing the points (-10, 11) and (10, -9) is/are:");
        System.out.println("     " + toString(intersection(makePoint(-10, 11), makePoint(10, -9),
                            center, radius, false)));
        System.out.println("\n    a segment starting at (-10, 11) and ending at (-11, 12) is/are");
        System.out.println("     " + toString(intersection(makePoint(-10, 11), makePoint(-11, 12),
                            center, radius, true)));
        System.out.println("\n    a horizontal line containing the points (3, -2) and (7, -2) is/are:");
        System.out.println("     " + toString(intersection(makePoint(3, -2), makePoint(7, -2), center, radius, false)));
        center.setLocation(0, 0);
        radius = 4.0;
        System.out.println("\n  A circle, center (0, 0) with radius 4, and:");
        System.out.println("\n    a vertical line containing the points (0, -3) and (0, 6) is/are:");
        System.out.println("     " + toString(intersection(makePoint(0, -3), makePoint(0, 6),
                            center, radius, false)));
        System.out.println("\n    a vertical segment starting at (0, -3) and ending at (0, 6) is/are:");
        System.out.println("     " + toString(intersection(makePoint(0, -3), makePoint(0, 6),
                            center, radius, true)));
        center.setLocation(4, 2);
        radius = 5.0;
        System.out.println("\n  A circle, center (4, 2) with radius 5, and:");
        System.out.println("\n    a line containing the points (6, 3) and (10, 7) is/are:");
        System.out.println("     " + toString(intersection(makePoint(6, 3), makePoint(10, 7),
                            center, radius, false)));
        System.out.println("\n    a segment starting at (7, 4) and ending at (11, 8) is/are:");
        System.out.println("     " + toString(intersection(makePoint(7, 4), makePoint(11, 8),
                            center, radius, true)));
    }

    private static Point2D makePoint(double x, double y) {
        return new Point2D.Double(x, y);
    }

    
    
    
    
    
    
    
    
    public static List<Point2D> intersection(Point2D p1, Point2D p2, Point2D center,
            double radius, boolean isSegment) throws NoninvertibleTransformException {
        List<Point2D> result = new ArrayList<>();
        double dx = p2.getX() - p1.getX();
        double dy = p2.getY() - p1.getY();
        AffineTransform trans = AffineTransform.getRotateInstance(dx, dy);
        trans.invert();
        trans.translate(-center.getX(), -center.getY());
        Point2D p1a = trans.transform(p1, null);
        Point2D p2a = trans.transform(p2, null);
        double y = p1a.getY();
        double minX = Math.min(p1a.getX(), p2a.getX());
        double maxX = Math.max(p1a.getX(), p2a.getX());
        if (y == radius || y == -radius) {
            if (!isSegment || (0 <= maxX && 0 >= minX)) {
                p1a.setLocation(0, y);
                trans.inverseTransform(p1a, p1a);
                result.add(p1a);
            }
        } else if (y < radius && y > -radius) {
            double x = Math.sqrt(radius * radius - y * y);
            if (!isSegment || (-x <= maxX && -x >= minX)) {
                p1a.setLocation(-x, y);
                trans.inverseTransform(p1a, p1a);
                result.add(p1a);
            }
            if (!isSegment || (x <= maxX && x >= minX)) {
                p2a.setLocation(x, y);
                trans.inverseTransform(p2a, p2a);
                result.add(p2a);
            }
        }
        return result;
    }

    public static String toString(Point2D point) {
        return String.format("(%g, %g)", point.getX(), point.getY());
    }

    public static String toString(List<Point2D> points) {
        StringBuilder str = new StringBuilder("[");
        for (int i = 0, n = points.size(); i < n; ++i) {
            if (i > 0)
                str.append(", ");
            str.append(toString(points.get(i)));
        }
        str.append("]");
        return str.toString();
    }
}