> And again I reply to my own post.
This is getting a bad habit. Sorry. This slightly modified version
uses the center of the two points with maximum distance as starting
point. For random sets, the results fit quite well most of the time --
on certain sets it's farther off than the mass center version though.
Regards,
Thomas.
def encircle(points) # takes array of Point objects
points = points.uniq
return if points.nil? or points.empty?
return Circle.new(points[0], 0) if points.size == 1
m, n = points.combination(2).sort_by {|a, b| -distance(a, b)}[0]
a = Point.new(m.x / 2 + n.x / 2, m.y / 2 + n.y / 2)
return Circle.new(a, distance(a, m)) unless points.size > 2
points = points.sort_by {|p| -distance(a, p)}
f = points[0]
df = distance(f, a)
b = med(f, a)
e = 1e-10
1000.times do
db = distance(f, b)
if points.all? {|p| distance(b, p) <= db + e}
da = distance(f, a)
if (da - db).abs <= e
return Circle.new(b, db)
else
a, b = b, med(f, b)
end
else
b = med(a, b)
end
end
raise RuntimeError
end
def med(a, b)
Point.new((b.x - a.x) / 2.0 + a.x, (b.y - a.y) / 2.0 + a.y)
end
def distance(p1, p2)
Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2)
end