public static Circle MakeCircle(IList <Point> points) { // 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 = Circle.INVALID; for (int i = 0; i < shuffled.Count; i++) { CustomPoint p = new CustomPoint(shuffled[i].X, shuffled[i].Y); if (c.r < 0 || !c.Contains(p)) { List <CustomPoint> shuffeledRange = new List <CustomPoint>(); foreach (var point in shuffled.GetRange(0, i + 1)) { shuffeledRange.Add(new CustomPoint(point.X, point.Y)); } c = MakeCircleOnePoint(shuffeledRange, p); } } return(c); }
public double Distance(CustomPoint p) { double dx = x - p.x; double dy = y - p.y; return(Math.Sqrt(dx * dx + dy * dy)); }
// Two boundary points known private static Circle MakeCircleTwoPoints(List <CustomPoint> points, CustomPoint p, CustomPoint q) { Circle circ = MakeDiameter(p, q); Circle left = Circle.INVALID; Circle right = Circle.INVALID; // For each point not in the two-point circle CustomPoint pq = q.Subtract(p); foreach (CustomPoint 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); } }
public static Circle MakeCircumcircle(CustomPoint a, CustomPoint b, CustomPoint c) { // Mathematical algorithm from Wikipedia: Circumscribed circle double ox = (Math.Min(Math.Min(a.x, b.x), c.x) + Math.Max(Math.Min(a.x, b.x), c.x)) / 2; double oy = (Math.Min(Math.Min(a.y, b.y), c.y) + Math.Max(Math.Min(a.y, b.y), c.y)) / 2; double ax = a.x - ox, ay = a.y - oy; double bx = b.x - ox, by = b.y - oy; double cx = c.x - ox, cy = c.y - oy; double d = (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) * 2; if (d == 0) { return(Circle.INVALID); } double x = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d; double y = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d; CustomPoint p = new CustomPoint(ox + x, oy + y); double r = Math.Max(Math.Max(p.Distance(a), p.Distance(b)), p.Distance(c)); return(new Circle(p, r)); }
private static Circle MakeCircleOnePoint(List <CustomPoint> points, CustomPoint p) { Circle c = new Circle(p, 0); for (int i = 0; i < points.Count; i++) { CustomPoint 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); }
// Signed area / determinant thing public double Cross(CustomPoint p) { return(x * p.y - y * p.x); }
public CustomPoint Subtract(CustomPoint p) { return(new CustomPoint(x - p.x, y - p.y)); }
public bool Contains(CustomPoint p) { return(c.Distance(p) <= r * MULTIPLICATIVE_EPSILON); }
public double r; // Radius public Circle(CustomPoint c, double r) { this.c = c; this.r = r; }
public static Circle MakeDiameter(CustomPoint a, CustomPoint b) { CustomPoint c = new CustomPoint((a.x + b.x) / 2, (a.y + b.y) / 2); return(new Circle(c, Math.Max(c.Distance(a), c.Distance(b)))); }