namespace CandyboxPlugin.Geometry { public class DefaultRectangle2Algorithm { public List FindRectangle2(List hullPoints) { //check if no bounding box available if (hullPoints.Count <= 1) return hullPoints; Rectangle2d minBox = null; var minAngle = 0d; //foreach edge of the convex hull for (var i = 0; i < hullPoints.Count; i++) { var nextIndex = i + 1; var current = hullPoints[i]; var next = hullPoints[nextIndex % hullPoints.Count]; //min / max points var top = double.MinValue; var bottom = double.MaxValue; var left = double.MaxValue; var right = double.MinValue; //get angle of segment to x axis var angle = AngleToXAxis(current,next); //rotate every point and get min and max values for each direction foreach (var p in hullPoints) { var rotatedPoint = RotateToXAxis(p, angle); top = Math.Max(top, rotatedPoint.Y); bottom = Math.Min(bottom, rotatedPoint.Y); left = Math.Min(left, rotatedPoint.X); right = Math.Max(right, rotatedPoint.X); } //create axis aligned bounding box var box = new Rectangle2d(new IntPoint((int)left, (int)bottom), new IntPoint((int)right, (int)top)); if (minBox == null || minBox.Area() > box.Area()) { minBox = box; minAngle = angle; } } //rotate axis algined box back var minimalBoundingBox = minBox.Points.Select(p => RotateToXAxis(p, -minAngle)).ToList(); return minimalBoundingBox; } /// /// Calculates the angle to the X axis. /// /// The angle to the X axis. /// The segment to get the angle from. static double AngleToXAxis(IntPoint a, IntPoint b) { var delta = a-b; return -Math.Atan((float)delta.Y / (float)delta.X); } /// /// Rotates vector by an angle to the x-Axis /// /// Rotated vector. /// Vector to rotate. /// Angle to trun by. static IntPoint RotateToXAxis(IntPoint v, double angle) { var newX = v.X * Math.Cos(angle) - v.Y * Math.Sin(angle); var newY = v.X * Math.Sin(angle) + v.Y * Math.Cos(angle); return new IntPoint((int)newX, (int)newY); } } public class Rectangle2d { public IntPoint Location { get; set; } public IntPoint Size { get; set; } public Rectangle2d() { } public Rectangle2d(IntPoint a, IntPoint c) : this() { Location = a; Size = c - a; } public double Area() { return Size.X * Size.Y; } public IntPoint[] Points { get { return new[] { new IntPoint (Location.X, Location.Y), new IntPoint (Location.X + Size.X, Location.Y), new IntPoint (Location.X + Size.X, Location.Y + Size.Y), new IntPoint (Location.X, Location.Y + Size.Y) }; } } } }