/// <summary> /// Returns the minimal distance between the polygon and the non- /// overlapping other supplied polygon. The minimal distance is /// always computed as the distance between a line segment and a /// point. The indices of the minimal distance configuration are /// returned in the out parameter, as the indices of points on the /// two polygons, and wether the line segement was on this or the /// other polygon. O(n). The returned index of the line segment is /// the lower point index (except in case of wraparound). /// </summary> public static double MinDistanceTo( this Polygon2d polygon, Polygon2d polygon1, out int pi0, out int pi1, out bool lineOnThis) { var p0a = polygon.m_pointArray; var p1a = polygon1.m_pointArray; var p0c = polygon.m_pointCount; var p1c = polygon1.m_pointCount; var e0a = polygon.GetEdgeArray(); var e1a = polygon1.GetEdgeArray(); int i0 = p0a.IndexOfMinY(p0c), i1 = p1a.IndexOfMaxY(p1c); V2d p0 = p0a[i0], e0 = e0a[i0], p1 = p1a[i1], e1 = e1a[i1]; int start0 = i0, start1 = i1; var dir = V2d.XAxis; var d = V2d.Distance(p0, p1); var bestValue = double.MaxValue; int bpi0 = -1, bpi1 = -1; var bLineOnThis = true; do { var s0 = Fun.Atan2(e0.Dot90(dir), e0.Dot(dir)); var s1 = Fun.Atan2(e1.Dot270(dir), e1.Dot180(dir)); if (s0 <= s1) { dir = e0a[i0]; int i0n = (i0 + 1) % p0c; var p0n = p0a[i0]; var dn = V2d.Distance(p0n, p1); var dist = DistanceToLine(p1, p0, p0n, d, dn); if (dist < bestValue) { bestValue = dist; bLineOnThis = true; bpi0 = i0; bpi1 = i1; } i0 = i0n; p0 = p0n; e0 = e0a[i0]; d = dn; } else { dir = e0a[i1].Rot180; int i1n = (i1 + 1) % p1c; var p1n = p1a[i1]; var dn = V2d.Distance(p0, p1n); var dist = DistanceToLine(p0, p1, p1n, d, dn); if (dist < bestValue) { bestValue = dist; bLineOnThis = false; bpi0 = i0; bpi1 = i1; } i1 = i1n; p1 = p1n; e1 = e1a[i1]; d = dn; } }while (i0 != start0 || i1 != start1); lineOnThis = bLineOnThis; pi0 = bpi0; pi1 = bpi1; return(bestValue); }
/// <summary> /// Returns the rotation of the supplied counter clockwise enumerated /// convex polygon that results in the minimum area enclosing box. /// If multiple rotations are within epsilon in their area, the one /// that is closest to an axis-aligned rotation (0, 90, 180, 270) is /// returned. O(n). /// </summary> public static M22d ComputeMinAreaEnclosingBoxRotation( this Polygon2d polygon, double epsilon = 1e-6) { polygon = polygon.WithoutMultiplePoints(epsilon); var pc = polygon.PointCount; if (pc < 2) { return(M22d.Identity); } var ea = polygon.GetEdgeArray(); ea.Apply(v => v.Normalized); int i0 = 0, i1 = 0; int i2 = 0, i3 = 0; var min = polygon[0]; var max = polygon[0]; for (int pi = 1; pi < pc; pi++) { var p = polygon[pi]; if (p.Y < min.Y) { i0 = pi; min.Y = p.Y; } else if (p.Y > max.Y) { i2 = pi; max.Y = p.Y; } if (p.X > max.X) { i1 = pi; max.X = p.X; } else if (p.X < min.X) { i3 = pi; min.X = p.X; } } V2d p0 = polygon[i0], e0 = ea[i0], p1 = polygon[i1], e1 = ea[i1]; V2d p2 = polygon[i2], e2 = ea[i2], p3 = polygon[i3], e3 = ea[i3]; int end0 = (i0 + 1) % pc, end1 = (i1 + 1) % pc; int end2 = (i2 + 1) % pc, end3 = (i3 + 1) % pc; var dir = V2d.XAxis; var best = dir; var bestArea = double.MaxValue; var bestValue = double.MaxValue; while (true) { var s0 = Fun.FastAtan2(e0.Dot90(dir), e0.Dot(dir)); var s1 = Fun.FastAtan2(e1.Dot180(dir), e1.Dot90(dir)); var s2 = Fun.FastAtan2(e2.Dot270(dir), e2.Dot180(dir)); var s3 = Fun.FastAtan2(e3.Dot(dir), e3.Dot270(dir)); int si, si01, si23; double s01, s23; if (s0 < s1) { s01 = s0; si01 = 0; } else { s01 = s1; si01 = 1; } if (s2 < s3) { s23 = s2; si23 = 2; } else { s23 = s3; si23 = 3; } if (s01 < s23) { si = si01; } else { si = si23; } if (si == 0) { dir = ea[i0]; } else if (si == 1) { dir = ea[i1].Rot270; } else if (si == 2) { dir = ea[i2].Rot180; } else { dir = ea[i3].Rot90; } double sx = (p2 - p0).Dot90(dir), sy = (p1 - p3).Dot(dir); double area = sx * sy; double value = Fun.Min(Fun.Abs(dir.X), Fun.Abs(dir.Y)); if (area < bestArea - epsilon || (area < bestArea + epsilon && value < bestValue)) { bestArea = area; bestValue = value; best = dir; } if (si == 0) { if (++i0 >= pc) { i0 -= pc; } if (i0 == end1) { break; } p0 = polygon[i0]; e0 = ea[i0]; } else if (si == 1) { if (++i1 >= pc) { i1 -= pc; } if (i1 == end2) { break; } p1 = polygon[i1]; e1 = ea[i1]; } else if (si == 2) { if (++i2 >= pc) { i2 -= pc; } if (i2 == end3) { break; } p2 = polygon[i2]; e2 = ea[i2]; } else { if (++i3 >= pc) { i3 -= pc; } if (i3 == end0) { break; } p3 = polygon[i3]; e3 = ea[i3]; } } return(new M22d(best.X, best.Y, -best.Y, best.X)); }