Solver = function() { return {
mec: Circle(),
points: [],
solve: function(a) {
a.sort(function(p1,p2) { return p1.compareTo(p2); }); for (var i in a) {
this.addPoint(a[i]);
} return this.mec;
},
direction: function(p0, p1, p2) { return (p0.x-p1.x)*(p2.y-p1.y) - (p2.x-p1.x)*(p0.y-p1.y);
},
halfHull: function(left, right, input, factor) {
input = input.slice(0);
input.push(right);
var half = [];
half.push(left); for (var i in input) {
half.push(input[i]);
//console.log("HALF: " + util.inspect(half) + " LENGTH: " + half.length); while (half.length >= 3) {
var len = half.length;
var dir = factor * this.direction(half[len-3], half[len-1], half[len-2]); if (dir <= 0) {
half.splice(len-2,1)
} else { break;
}
}
} return half;
},
convexHull: function() {
var left = this.points[0];
var a = this.points.slice(1);
var right = a.pop();
var upper = [];
var lower = []; for (var i in a) {
var p = a[i];
var dir = this.direction(left, right, p); if (dir < 0) {
upper.push(p);
} else {
lower.push(p);
}
}
var upper_hull = this.halfHull(left, right, upper, -1);
var lower_hull = this.halfHull(left, right, lower, 1);
var combined = upper_hull.concat(lower_hull);
var ret = []; for (var i in combined) {
var e = combined[i];
var has = false; for (var j in ret) { if (ret[j].equalTo(e)) {
has = true; break;
}
} if (!has) {
ret.push(e);
}
} return ret;
},
addPoint: function(point) {
this.points.push(point); if (this.mec.center) { if (!this.mec.contains(point)) {
this.iterate();
}
} else {
this.iterate();
}
},
iterate: function() { if (this.points.length == 1) {
this.mec.center = this.points[0];
this.mec.radius = 0;
} elseif (this.points.length == 2) {
this.mec.center = this.points[0].center(this.points[1]);
this.mec.radius = this.points[0].distance(this.mec.center);
} else {
var a = this.convexHull();
var point_a = a[0];
var point_c = a[1]; while (true) {
var point_b = null;
var best_theta = 180.0; for (var i in this.points) {
var point = this.points[i]; if (point != point_a && point != point_c) {
var theta_abc = point.angleBetween(point_a, point_c); if (theta_abc < best_theta) {
point_b = point;
best_theta = theta_abc;
}
}
} if (best_theta >= 90.0 || point_b == null) {
this.mec.center = point_a.center(point_c);
this.mec.radius = point_a.distance(this.mec.center); return this.mec;
}
var ang_bca = point_c.angleBetween(point_b, point_a);
var ang_cab = point_a.angleBetween(point_c, point_b); if (ang_bca > 90.0) {
point_c = point_b;
} elseif (ang_cab <= 90.0) { break;
} else {
point_a = point_b;
}
}
var ch1 = difference(point_b, point_a).scale(0.5);
var ch2 = difference(point_c, point_a).scale(0.5);
var n1 = ch1.normalLeft();
var n2 = ch2.normalLeft();
ch1 = sum(point_a, ch1);
ch2 = sum(point_a, ch2);
this.mec.center = infLineIntersection(ch1, n1, ch2, n2);
this.mec.radius = this.mec.center.distance(point_a);
} return this.mec;
}
};
}
var s = Solver();
console.log("Solver: " + util.inspect(s));
for (var iter = 0; iter < 5; iter++) {
var start = newDate().getTime();
var n = Math.pow(10,iter); for (var j = 0; j < 25; j++) {
var a = []; for (var i = 0; i < n; i++) {
a.push(randomPoint());
}
var circle = s.solve(a);
}
var stop = newDate().getTime();
var delta = (stop - start) / 1000.0;
console.log("Points: " + n + ": " + delta);
}