/// <summary> /// Algorithm to find a minimal bounding rectangle (MBR) such that the MBR corresponds to a rectangle /// with smallest possible area completely enclosing the polygon. /// <para>From 'A Fast Algorithm for Generating a Minimal Bounding Rectangle' by Lennert D. Den Boer.</para> /// </summary> /// <param name="polygon"> /// Polygon P is assumed to be both simple and convex, and to contain no duplicate (coincident) vertices. /// The vertices of P are assumed to be in strict cyclic sequential order, either clockwise or /// counter-clockwise relative to the origin P0. /// </param> internal static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList <PdfPoint> polygon) { if (polygon == null || polygon.Count == 0) { throw new ArgumentException("ParametricPerpendicularProjection(): polygon cannot be null and must contain at least one point."); } if (polygon.Count < 4) { if (polygon.Count == 1) { return(new PdfRectangle(polygon[0], polygon[0])); } else if (polygon.Count == 2) { return(new PdfRectangle(polygon[0], polygon[1])); } else { PdfPoint p3 = polygon[0].Add(polygon[1].Subtract(polygon[2])); return(new PdfRectangle(p3, polygon[1], polygon[0], polygon[2])); } } PdfPoint[] MBR = new PdfPoint[0]; double Amin = double.MaxValue; double tmin = 1; double tmax = 0; double smax = 0; int j = 1; int k = 0; int l = -1; PdfPoint Q = new PdfPoint(); PdfPoint R0 = new PdfPoint(); PdfPoint R1 = new PdfPoint(); PdfPoint u = new PdfPoint(); int nv = polygon.Count; while (true) { var Pk = polygon[k]; PdfPoint v = polygon[j].Subtract(Pk); double r = 1.0 / v.DotProduct(v); for (j = 0; j < nv; j++) { if (j == k) { continue; } PdfPoint Pj = polygon[j]; u = Pj.Subtract(Pk); double t = u.DotProduct(v) * r; PdfPoint Pt = new PdfPoint(t * v.X + Pk.X, t * v.Y + Pk.Y); u = Pt.Subtract(Pj); double s = u.DotProduct(u); if (t < tmin) { tmin = t; R0 = Pt; } if (t > tmax) { tmax = t; R1 = Pt; } if (s > smax) { smax = s; Q = Pt; l = j; } } if (l == -1) { // All points are colinear - rectangle has no area (need more tests) var bottomLeft = polygon.OrderBy(p => p.X).ThenBy(p => p.Y).First(); var topRight = polygon.OrderByDescending(p => p.Y).OrderByDescending(p => p.X).First(); return(new PdfRectangle(bottomLeft, topRight, bottomLeft, topRight)); } PdfPoint PlMinusQ = polygon[l].Subtract(Q); PdfPoint R2 = R1.Add(PlMinusQ); PdfPoint R3 = R0.Add(PlMinusQ); u = R1.Subtract(R0); double A = u.DotProduct(u) * smax; if (A < Amin) { Amin = A; MBR = new[] { R0, R1, R2, R3 }; } k++; j = k; if (j == nv) { j = 0; } if (k == nv) { break; } } return(new PdfRectangle(MBR[2], MBR[3], MBR[1], MBR[0])); }
/// <summary> /// Algorithm to find a minimal bounding rectangle (MBR) such that the MBR corresponds to a rectangle /// with smallest possible area completely enclosing the polygon. /// <para>From 'A Fast Algorithm for Generating a Minimal Bounding Rectangle' by Lennert D. Den Boer.</para> /// </summary> /// <param name="polygon"> /// Polygon P is assumed to be both simple and convex, and to contain no duplicate (coincident) vertices. /// The vertices of P are assumed to be in strict cyclic sequential order, either clockwise or /// counter-clockwise relative to the origin P0. /// </param> private static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList <PdfPoint> polygon) { if (polygon == null || polygon.Count == 0) { throw new ArgumentException("ParametricPerpendicularProjection(): polygon cannot be null and must contain at least one point.", nameof(polygon)); } else if (polygon.Count == 1) { return(new PdfRectangle(polygon[0], polygon[0])); } else if (polygon.Count == 2) { return(new PdfRectangle(polygon[0], polygon[1])); } PdfPoint[] MBR = new PdfPoint[0]; double Amin = double.PositiveInfinity; int j = 1; int k = 0; PdfPoint Q = new PdfPoint(); PdfPoint R0 = new PdfPoint(); PdfPoint R1 = new PdfPoint(); PdfPoint u = new PdfPoint(); while (true) { PdfPoint Pk = polygon[k]; PdfPoint v = polygon[j].Subtract(Pk); double r = 1.0 / v.DotProduct(v); double tmin = 1; double tmax = 0; double smax = 0; int l = -1; for (j = 0; j < polygon.Count; j++) { PdfPoint Pj = polygon[j]; u = Pj.Subtract(Pk); double t = u.DotProduct(v) * r; PdfPoint Pt = new PdfPoint(t * v.X + Pk.X, t * v.Y + Pk.Y); u = Pt.Subtract(Pj); double s = u.DotProduct(u); if (t < tmin) { tmin = t; R0 = Pt; } if (t > tmax) { tmax = t; R1 = Pt; } if (s > smax) { smax = s; Q = Pt; l = j; } } if (l != -1) { PdfPoint PlMinusQ = polygon[l].Subtract(Q); PdfPoint R2 = R1.Add(PlMinusQ); PdfPoint R3 = R0.Add(PlMinusQ); u = R1.Subtract(R0); double A = u.DotProduct(u) * smax; if (A < Amin) { Amin = A; MBR = new[] { R0, R1, R2, R3 }; } } k++; j = k + 1; if (j == polygon.Count) { j = 0; } if (k == polygon.Count) { break; } } return(new PdfRectangle(MBR[2], MBR[3], MBR[1], MBR[0])); }