
Given two points on a plane and a radius, usually two circles of given radius can be drawn through the points.

Exceptions

 r==0.0 should be treated as never describing circles (except in the case where the points are coincident).
 If the points are coincident then an infinite number of circles with the point on their circumference can be drawn, unless r==0.0 as well which then collapses the circles to a point.
 If the points form a diameter then return two identical circles or return a single circle, according to which is the most natural mechanism for the implementation language.
 If the points are too far apart then no circles can be drawn.


Task detail

 Write a function/subroutine/method/... that takes two points and a radius and returns the two circles through those points, or some indication of special cases where two, possibly equal, circles cannot be returned.
 Show here the output for the following inputs:
      p1                p2           r
0.1234, 0.9876    0.8765, 0.2345    2.0
0.0000, 2.0000    0.0000, 0.0000    1.0
0.1234, 0.9876    0.1234, 0.9876    2.0
0.1234, 0.9876    0.8765, 0.2345    0.5
0.1234, 0.9876    0.1234, 0.9876    0.0



Related task

   Total circles area.


See also

   Finding the Center of a Circle from 2 Points and Radius from Math forum @ Drexel


 
(* Task : Circles of given radius through two points *)
 
(* Types to make code even more readable *)
type point = float * float
type radius = float
type circle = Circle of radius * point
type circ_output = 
    NoSolution
    | OneSolution of circle
    | TwoSolutions of circle * circle
    | InfiniteSolutions
;;
 
(* Actual function *)
let circles_2points_radius (x1, y1 : point) (x2, y2 : point) (r : radius) =
    let (dx, dy) = (x2 -. x1, y2 -. y1) in
    let dist_sq = dx *. dx +. dy *. dy in
    match dist_sq, r with
    (* Edge case - point circles *)
    | 0., 0. -> OneSolution (Circle (r, (x1, y1)))
    (* Edge case - coinciding points *)
    | 0., _ -> InfiniteSolutions
    | _ -> 
        let side_len_sq = r *. r -. dist_sq /. 4. in
        let midp = ((x1 +. x2) *. 0.5, (y1 +. y2) *. 0.5) in
        (* Points are too far apart; same whether r = 0 or not *)
        if side_len_sq < 0. then NoSolution
        (* Points are on diameter *)
        else if side_len_sq = 0. then OneSolution (Circle (r, midp))
        else
            (* A right-angle triangle is made with the radius as hyp, dist/2 as one side *)
            let side_len = sqrt (r *. r -. dist_sq /. 4.) in
            let dist = sqrt dist_sq in
            (*  A 90-deg rotation of a vector (x, y) is obtained by either (y, -x) or (-y, x) 
                We need both, so pick one and the other is its negative.
            *)
            let (vx, vy) = (-. dy *. side_len /. dist, dx *. side_len /. dist) in
            let c1 = Circle (r, (fst midp +. vx, snd midp +. vy)) in
            let c2 = Circle (r, (fst midp -. vx, snd midp -. vy)) in
            TwoSolutions (c1, c2)
;;
 
(* Relevant tests and printing *)
let tests = [
    (0.1234, 0.9876), (0.8765, 0.2345), 2.0;
    (0.0000, 2.0000), (0.0000, 0.0000), 1.0;
    (0.1234, 0.9876), (0.1234, 0.9876), 2.0;
    (0.1234, 0.9876), (0.8765, 0.2345), 0.5;
    (0.1234, 0.9876), (0.1234, 0.9876), 0.0;
] ;;
 
let format_output (out : circ_output) = match out with
    | NoSolution -> print_endline "No solution"
    | OneSolution (Circle (_, (x, y))) -> Printf.printf "One solution: (%.6f, %.6f)\n" x y
    | TwoSolutions (Circle (_, (x1, y1)), Circle (_, (x2, y2))) ->
        Printf.printf "Two solutions: (%.6f, %.6f) and (%.6f, %.6f)\n" x1 y1 x2 y2
    | InfiniteSolutions -> print_endline "Infinite solutions"
;;
 
let _ = 
    List.iter
    (fun (a, b, c) -> circles_2points_radius a b c |> format_output)
    tests
;;
 