
Task

Implement a solution to the Problem of Apollonius   (description on Wikipedia)   which is the problem of finding the circle that is tangent to three specified circles   (colored black in the diagram below to the right).

There is an   algebraic solution   which is pretty straightforward.



The solutions to the example in the code are shown in the diagram   (below and to the right).

The red circle is "internally tangent" to all three black circles,   and the green circle is "externally tangent" to all three black circles.




class Circle
  def initialize(x, y, r)
    @x, @y, @r = [x, y, r].map(&:to_f)
  end
  attr_reader :x, :y, :r
 
  def self.apollonius(c1, c2, c3, s1=1, s2=1, s3=1)
    x1, y1, r1 = c1.x, c1.y, c1.r
    x2, y2, r2 = c2.x, c2.y, c2.r
    x3, y3, r3 = c3.x, c3.y, c3.r
 
    v11 = 2*x2 - 2*x1
    v12 = 2*y2 - 2*y1
    v13 = x1**2 - x2**2 + y1**2 - y2**2 - r1**2 + r2**2
    v14 = 2*s2*r2 - 2*s1*r1
 
    v21 = 2*x3 - 2*x2
    v22 = 2*y3 - 2*y2
    v23 = x2**2 - x3**2 + y2**2 - y3**2 - r2**2 + r3**2
    v24 = 2*s3*r3 - 2*s2*r2
 
    w12 = v12/v11
    w13 = v13/v11
    w14 = v14/v11
 
    w22 = v22/v21 - w12
    w23 = v23/v21 - w13
    w24 = v24/v21 - w14
 
    p = -w23/w22
    q = w24/w22
    m = -w12*p - w13
    n = w14 - w12*q
 
    a = n**2 + q**2 - 1
    b = 2*m*n - 2*n*x1 + 2*p*q - 2*q*y1 + 2*s1*r1
    c = x1**2 + m**2 - 2*m*x1 + p**2 + y1**2 - 2*p*y1 - r1**2
 
    d = b**2 - 4*a*c
    rs = (-b - Math.sqrt(d)) / (2*a)
    xs = m + n*rs
    ys = p + q*rs
 
    self.new(xs, ys, rs)
  end
 
  def to_s
    "Circle: x=#{@x}, y=#{@y}, r=#{@r}"
  end
end
 
puts c1 = Circle.new(0, 0, 1)
puts c2 = Circle.new(2, 4, 2)
puts c3 = Circle.new(4, 0, 1)
 
puts Circle.apollonius(c1, c2, c3)
puts Circle.apollonius(c1, c2, c3, -1, -1, -1)