Smallest enclosing circle problem: Difference between revisions

Content added Content deleted
(Added Wren)
Line 201: Line 201:
Center is at: [0.0405866077655439, 0.5556683897981481, -0.2313678300507679, 0.6074066023194586, -0.2003463948612026]
Center is at: [0.0405866077655439, 0.5556683897981481, -0.2313678300507679, 0.6074066023194586, -0.2003463948612026]
Radius is: 2.7946020036995454
Radius is: 2.7946020036995454
</pre>

=={{header|Wren}}==
Well a circle is a two dimensional figure and so, despite any contradictory indications in the task description, that's what this solution provides.

It is based on Wezl's algorithm and follows closely the C++ code [https://www.geeksforgeeks.org/minimum-enclosing-circle-set-2-welzls-algorithm/?ref=rp here].
<lang ecmascript>import "random" for Random

var Rand = Random.new()

class Point {
// returns the square of the distance between two points
static distSq(a, b) { (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y) }

// returns the center of a circle defined by 3 points
static getCircleCenter(bx, by, cx, cy) {
var b = bx*bx + by*by
var c = cx*cx + cy*cy
var d = bx*cy - by*cx
return Point.new((cy*b - by*c) / (2 * d), (bx*c - cx*b) / (2 * d))
}

// constructs a new Point object
construct new(x, y) {
_x = x
_y = y
}

// basic properties
x { _x }
x=(o) { _x = o }
y { _y }
y=(o) { _y = o }

// returns a copy of the current instance
copy() { Point.new(_x, _y) }

// string representation
toString { "(%(_x), %(_y))" }
}

class Circle {
// returns a unique circle that intersects 3 points
static from(a, b, c) {
var i = Point.getCircleCenter(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y)
i.x = i.x + a.x
i.y = i.y + a.y
return Circle.new(i, Point.distSq(i, a).sqrt)
}

// returns smallest circle that intersects 2 points
static from(a, b) {
var c = Point.new((a.x + b.x) / 2, (a.y + b.y) / 2)
return Circle.new(c, Point.distSq(a, b).sqrt/2)
}

// returns smallest enclosing circle for n <= 3
static secTrivial(rs) {
var size = rs.count
if (size > 3) Fiber.abort("There shouldn't be more than 3 points.")
if (size == 0) return Circle.new(Point.new(0, 0), 0)
if (size == 1) return Circle.new(rs[0], 0)
if (size == 2) return Circle.from(rs[0], rs[1])
for (i in 0..1) {
for (j in i+1..2) {
var c = Circle.from(rs[i], rs[j])
if (c.encloses(rs)) return c
}
}
return Circle.from(rs[0], rs[1], rs[2])
}

// private helper method for Welzl method
static welzl_(ps, rs, n) {
rs = rs.toList // passed by value so need to copy
if (n == 0 || rs.count == 3) return secTrivial(rs)
var idx = Rand.int(n)
var p = ps[idx]
ps[idx] = ps[n-1]
ps[n-1] = p
var d = welzl_(ps, rs, n-1)
if (d.contains(p)) return d
rs.add(p)
return welzl_(ps, rs, n-1)
}

// applies the Welzl algorithm to find the SEC
static welzl(ps) {
var pc = List.filled(ps.count, null)
for (i in 0...pc.count) pc[i] = ps[i].copy()
Rand.shuffle(pc)
return welzl_(pc, [], pc.count)
}

// constructs a new Circle object from its center point and its radius
construct new(c, r) {
_c = c.copy()
_r = r
}

// basic properties
c { _c }
r { _r }

// returns whether the current instance contains the point 'p'
contains(p) { Point.distSq(_c, p) <= _r * _r }

// returns whether the current instance contains a list of points
encloses(ps) {
for (p in ps) if (!contains(p)) return false
return true
}

// string representation
toString { "Center %(_c), Radius %(_r)" }
}

var tests = [
[Point.new(0, 0), Point.new(0, 1), Point.new(1, 0)],
[Point.new(5, -2), Point.new(-3, -2), Point.new(-2, 5), Point.new(1, 6), Point.new(0, 2)]
]

for (test in tests) System.print(Circle.welzl(test))</lang>

{{out}}
<pre>
Center (0.5, 0.5), Radius 0.70710678118655
Center (1, 1), Radius 5
</pre>
</pre>