/* * Returns the smallest circle that encloses all the given points. Runs in expected O(n) time, randomized. * Note: If 0 points are given, a circle of radius -1 is returned. If 1 point is given, a circle of radius 0 is returned. */ // Initially: No boundary points known public static Circle GetMinimumCoverCircle(List <Dot> dots) { List <Point> points = new List <Point>(); foreach (Dot d in dots) { points.Add(new Point(d.Position.X, d.Position.Y)); } // Clone list to preserve the caller's data, do Durstenfeld shuffle List <Point> shuffled = new List <Point>(points); Random rand = new Random(); for (int i = shuffled.Count - 1; i > 0; i--) { int j = rand.Next(i + 1); Point temp = shuffled[i]; shuffled[i] = shuffled[j]; shuffled[j] = temp; } // Progressively add points to circle or recompute circle Circle c = new Circle(new Point(0, 0), -1); for (int i = 0; i < shuffled.Count; i++) { Point p = shuffled[i]; if (c.r < 0 || !c.Contains(p)) { c = MakeCircleOnePoint(shuffled.GetRange(0, i + 1), p); } } return(c); }
// Two boundary points known private static Circle MakeCircleTwoPoints(List <Point> points, Point p, Point q) { Circle circ = MakeDiameter(p, q); Circle left = new Circle(new Point(0, 0), -1); Circle right = new Circle(new Point(0, 0), -1); // For each point not in the two-point circle Point pq = q.Subtract(p); foreach (Point r in points) { if (circ.Contains(r)) { continue; } // Form a circumcircle and classify it on left or right side double cross = pq.Cross(r.Subtract(p)); Circle c = MakeCircumcircle(p, q, r); if (c.r < 0) { continue; } else if (cross > 0 && (left.r < 0 || pq.Cross(c.c.Subtract(p)) > pq.Cross(left.c.Subtract(p)))) { left = c; } else if (cross < 0 && (right.r < 0 || pq.Cross(c.c.Subtract(p)) < pq.Cross(right.c.Subtract(p)))) { right = c; } } // Select which circle to return if (left.r < 0 && right.r < 0) { return(circ); } else if (left.r < 0) { return(right); } else if (right.r < 0) { return(left); } else { return(left.r <= right.r ? left : right); } }
// One boundary point known private static Circle MakeCircleOnePoint(List <Point> points, Point p) { Circle c = new Circle(p, 0); for (int i = 0; i < points.Count; i++) { Point q = points[i]; if (!c.Contains(q)) { if (c.r == 0) { c = MakeDiameter(p, q); } else { c = MakeCircleTwoPoints(points.GetRange(0, i + 1), p, q); } } } return(c); }