Example #1
0
        /// <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);
        }
Example #2
0
        /// <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));
        }