Task: Line circle intersection


C++: #include <iostream>
#include <utility>
#include <vector>

using Point = std::pair<double, double>;
constexpr auto eps = 1e-14;

std::ostream &operator<<(std::ostream &os, const Point &p) {
    auto x = p.first;
    if (x == 0.0) {
        x = 0.0;
    }
    auto y = p.second;
    if (y == 0.0) {
        y = 0.0;
    }
    return os << '(' << x << ", " << y << ')';
}

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    auto itr = v.cbegin();
    auto end = v.cend();

    os << '[';
    if (itr != end) {
        os << *itr;
        itr = std::next(itr);
    }
    while (itr != end) {
        os << ", " << *itr;
        itr = std::next(itr);
    }
    return os << ']';
}

double sq(double x) {
    return x * x;
}

std::vector<Point> intersects(const Point &p1, const Point &p2, const Point &cp, double r, bool segment) {
    std::vector<Point> res;
    auto x0 = cp.first;
    auto y0 = cp.second;
    auto x1 = p1.first;
    auto y1 = p1.second;
    auto x2 = p2.first;
    auto y2 = p2.second;
    auto A = y2 - y1;
    auto B = x1 - x2;
    auto C = x2 * y1 - x1 * y2;
    auto a = sq(A) + sq(B);
    double b, c;
    bool bnz = true;
    if (abs(B) >= eps) {
        b = 2 * (A * C + A * B * y0 - sq(B) * x0);
        c = sq(C) + 2 * B * C * y0 - sq(B) * (sq(r) - sq(x0) - sq(y0));
    } else {
        b = 2 * (B * C + A * B * x0 - sq(A) * y0);
        c = sq(C) + 2 * A * C * x0 - sq(A) * (sq(r) - sq(x0) - sq(y0));
        bnz = false;
    }
    auto d = sq(b) - 4 * a * c; 
    if (d < 0) {
        return res;
    }

    
    auto within = [x1, y1, x2, y2](double x, double y) {
        auto d1 = sqrt(sq(x2 - x1) + sq(y2 - y1));  
        auto d2 = sqrt(sq(x - x1) + sq(y - y1));    
        auto d3 = sqrt(sq(x2 - x) + sq(y2 - y));    
        auto delta = d1 - d2 - d3;
        return abs(delta) < eps;                    
    };

    auto fx = [A, B, C](double x) {
        return -(A * x + C) / B;
    };

    auto fy = [A, B, C](double y) {
        return -(B * y + C) / A;
    };

    auto rxy = [segment, &res, within](double x, double y) {
        if (!segment || within(x, y)) {
            res.push_back(std::make_pair(x, y));
        }
    };

    double x, y;
    if (d == 0.0) {
        
        if (bnz) {
            x = -b / (2 * a);
            y = fx(x);
            rxy(x, y);
        } else {
            y = -b / (2 * a);
            x = fy(y);
            rxy(x, y);
        }
    } else {
        
        d = sqrt(d);
        if (bnz) {
            x = (-b + d) / (2 * a);
            y = fx(x);
            rxy(x, y);
            x = (-b - d) / (2 * a);
            y = fx(x);
            rxy(x, y);
        } else {
            y = (-b + d) / (2 * a);
            x = fy(y);
            rxy(x, y);
            y = (-b - d) / (2 * a);
            x = fy(y);
            rxy(x, y);
        }
    }

    return res;
}

int main() {
    std::cout << "The intersection points (if any) between:\n";

    auto cp = std::make_pair(3.0, -5.0);
    auto r = 3.0;
    std::cout << "  A circle, center " << cp << " with radius " << r << ", and:\n";

    auto p1 = std::make_pair(-10.0, 11.0);
    auto p2 = std::make_pair(10.0, -9.0);
    std::cout << "    a line containing the points " << p1 << " and " << p2 << " is/are:\n";
    std::cout << "     " << intersects(p1, p2, cp, r, false) << '\n';

    p2 = std::make_pair(-10.0, 12.0);
    std::cout << "    a segment starting at " << p1 << " and ending at " << p2 << " is/are:\n";
    std::cout << "     " << intersects(p1, p2, cp, r, true) << '\n';

    p1 = std::make_pair(3.0, -2.0);
    p2 = std::make_pair(7.0, -2.0);
    std::cout << "    a horizontal line containing the points " << p1 << " and " << p2 << " is/are:\n";
    std::cout << "     " << intersects(p1, p2, cp, r, false) << '\n';

    cp = std::make_pair(0.0, 0.0);
    r = 4.0;
    std::cout << "  A circle, center " << cp << " with radius " << r << ", and:\n";

    p1 = std::make_pair(0.0, -3.0);
    p2 = std::make_pair(0.0, 6.0);
    std::cout << "    a vertical line containing the points " << p1 << " and " << p2 << " is/are:\n";
    std::cout << "     " << intersects(p1, p2, cp, r, false) << '\n';
    std::cout << "    a vertical segment containing the points " << p1 << " and " << p2 << " is/are:\n";
    std::cout << "     " << intersects(p1, p2, cp, r, true) << '\n';

    cp = std::make_pair(4.0, 2.0);
    r = 5.0;
    std::cout << "  A circle, center " << cp << " with radius " << r << ", and:\n";

    p1 = std::make_pair(6.0, 3.0);
    p2 = std::make_pair(10.0, 7.0);
    std::cout << "    a line containing the points " << p1 << " and " << p2 << " is/are:\n";
    std::cout << "     " << intersects(p1, p2, cp, r, false) << '\n';

    p1 = std::make_pair(7.0, 4.0);
    p2 = std::make_pair(11.0, 8.0);
    std::cout << "    a segment starting at " << p1 << " and ending at " << p2 << " is/are:\n";
    std::cout << "     " << intersects(p1, p2, cp, r, true) << '\n';

    return 0;
}


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