Пример #1
0
        /// <summary>
        /// Returns:
        ///      1 if the Polygon created by poly has no self-intersections
        ///      0 if one point of the polygon lies close to a line (absoluteEpsilon)
        ///     -1 if the Polygon created by poly has a real self-intersection
        /// </summary>
        public static int HasSelfIntersections(
            this Polygon2d poly, double absoluteEpsilon)
        {
            var pointCount = poly.PointCount;

            if (absoluteEpsilon < 0.0)
            {
                throw new ArgumentOutOfRangeException();
            }
            int i     = 0;
            int u1    = 0;
            int worst = 1;
            V2d n0;

            //Triangles cannot have self-intersections
            if (pointCount == 3)
            {
                return(1);
            }

            Line2d line;

            for (i = 0; i < pointCount - 1; i++)
            {
                //line between i and i+1
                line = new Line2d(poly[i], poly[i + 1]);
                n0   = line.Direction.Normalized;
                n0   = new V2d(-n0.Y, n0.X);


                for (int u = i + 2; u < pointCount && (u + 1) % pointCount != i; u++)
                {
                    //Polygon not degenerated -> Line cannot intersect with line directly before and directly after
                    //All lines prior to (u,u1) have already been tested with (i,i+1)
                    u1 = (u + 1) % pointCount;

                    if (line.IntersectsLine(poly[u], poly[u1]))
                    {
                        //One Point of (u,u1) lies on line (within absoluteEpsilon)
                        if (n0.Dot(poly[u1] - line.P0).Abs() < absoluteEpsilon || n0.Dot(poly[u] - line.P0).Abs() < absoluteEpsilon)
                        {
                            worst = 0;
                        }
                        else
                        {
                            return(-1);
                        }
                    }
                }
            }

            return(worst);
        }
Пример #2
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);
        }
Пример #3
0
        private static double DistanceToLine(V2d query, V2d p0, V2d p1, double d0, double d1)
        {
            var p0p1 = p1 - p0;
            var p0q  = query - p0;
            var t    = V2d.Dot(p0q, p0p1);

            if (t <= 0)
            {
                return(d0);
            }
            var denom = p0p1.LengthSquared;

            if (t >= denom)
            {
                return(d1);
            }
            t /= denom;
            return(V2d.Distance(query, p0 + t * p0p1));
        }
Пример #4
0
        /// <summary>
        /// Returns true if the Polygon contains a degenerated part
        /// NonDegenerated holds the Non-Degenerated part of the polygon
        /// </summary>
        public static bool PolygonHasDegeneratedPart(this Polygon2d poly, double absoluteEpsilon, out int[] NonDegenerated)
        {
            if (absoluteEpsilon < 0.0)
            {
                throw new ArgumentOutOfRangeException();
            }

            int  i     = 0;
            int  u     = 1;
            int  start = 0;
            V2d  e0;
            V2d  e1;
            bool found = false;
            int  count = poly.PointCount;

            List <int> newPoints = new List <int>();

            newPoints.Add(0);

            double l0 = 0.0;

            while (i < count - 1)
            {
                e0 = (poly[i + 1] - poly[i]);
                l0 = e0.Length;

                e0 = e0 / l0;

                u = i + 1;
                V2d e = poly[(u + 1) % count] - poly[u];
                while (
                    (((e0.X * e.Y - e0.Y * e.X).Abs() < absoluteEpsilon && e.Dot(e0) > 0.0) ||
                     (poly[(u + 1) % count] - poly[i + 1]).Length < absoluteEpsilon) &&
                    u < count - 1)
                {
                    if ((poly[(u + 1) % count] - poly[i + 1]).Length < absoluteEpsilon)
                    {
                        found = true;
                    }

                    u++;
                    e0 = (poly[u] - poly[i]).Normalized;
                    e  = poly[(u + 1) % count] - poly[u];
                }
                u--;

                start = u;

                do
                {
                    u++;
                    e1 = (poly[(u + 1) % count] - poly[u]);
                }while (u < count && ((e0.X * e1.Y - e0.Y * e1.X).Abs() < absoluteEpsilon && e.Dot(e0) < 0.0));

                if (u != start + 1)
                {
                    found = true;
                }

                newPoints.Add(u);

                i = u;
            }

            NonDegenerated = newPoints.ToArray();
            return(found);
        }
Пример #5
0
        // 2-Dimensional

        #region V2d - V2d

        public static bool IsOrthogonalTo(this V2d u, V2d v) => Fun.IsTiny(u.Dot(v));
Пример #6
0
 /// <summary>
 /// Creates plane from point and normal vector. IMPORTANT: The
 /// supplied vector has to be normalized in order for all methods
 /// to work correctly, however if only relative height computations
 /// using the <see cref="Height"/> method are necessary, the normal
 /// vector need not be normalized.
 /// </summary>
 public Plane2d(V2d normalizedNormal, V2d point)
 {
     Normal   = normalizedNormal;
     Distance = V2d.Dot(normalizedNormal, point);
 }
Пример #7
0
        /// <summary>
        /// Projets the given point x perpendicular on the plane
        /// and returns the nearest point on the plane.
        /// </summary>
        public V2d NearestPoint(V2d x)
        {
            var p = Point;

            return(x - Normal.Dot(x - p) * Normal);
        }
Пример #8
0
 /// <summary>
 /// The signed height of the supplied point over the plane.
 /// </summary>
 public double Height(V2d p)
 {
     return(V2d.Dot(Normal, p) - Distance);
 }
Пример #9
0
 /// <summary>
 /// Gets the point on the ray that is closest to the given point.
 /// Ray direction must be normalized (length 1).
 /// </summary>
 public V2d GetClosestPointOnRay(V2d p)
 => Origin + Direction * Direction.Dot(p - Origin);
Пример #10
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));
        }
Пример #11
0
        /// <summary>
        /// Returns the Line-Segments of line inside the Polygon (CCW ordered).
        /// Works only with Convex-Polygons
        /// </summary>
        public static Line2d ClipWithConvex(this Line2d line, Polygon2d poly)
        {
            V2d  p = V2d.NaN;
            bool i0, i1;

            i0 = poly.Contains(line.P0);
            i1 = poly.Contains(line.P1);

            if (i0 && i1)
            {
                return(line);
            }
            else if ((!i0 && i1) || (i0 && !i1))
            {
                foreach (var l in poly.EdgeLines)
                {
                    if (line.Intersects(l, out p))
                    {
                        break;
                    }
                }

                if (i0)
                {
                    return(new Line2d(line.P0, p));
                }
                else
                {
                    return(new Line2d(p, line.P1));
                }
            }
            else
            {
                V2d p0 = V2d.NaN;
                V2d p1 = V2d.NaN;
                int c  = 0;

                foreach (var l in poly.EdgeLines)
                {
                    if (line.Intersects(l, out p))
                    {
                        if (c == 0)
                        {
                            p0 = p;
                        }
                        else
                        {
                            p1 = p;
                        }
                        c++;
                    }
                }

                if (c == 2)
                {
                    V2d u = p1 - p0;

                    if (u.Dot(line.Direction) > 0)
                    {
                        return(new Line2d(p0, p1));
                    }
                    else
                    {
                        return(new Line2d(p1, p0));
                    }
                }
                else
                {
                    return(new Line2d(V2d.NaN, V2d.NaN));
                }
            }
        }
Пример #12
0
        /// <summary>
        /// Returns the Line-Segments of line inside the Polygon (CCW ordered).
        /// Works with all (convex and non-convex) Polygons
        /// </summary>
        public static IEnumerable <Line2d> ClipWith(this Line2d line, Polygon2d poly)
        {
            bool i0, i1;

            i0 = poly.Contains(line.P0);
            i1 = poly.Contains(line.P1);


            List <V2d>  resulting = new List <V2d>();
            List <bool> enter     = new List <bool>();

            if (i0)
            {
                resulting.Add(line.P0);
                enter.Add(true);
            }
            if (i1)
            {
                resulting.Add(line.P1);
                enter.Add(false);
            }

            V2d p         = V2d.NaN;
            V2d direction = line.Direction;

            foreach (var l in poly.EdgeLines)
            {
                if (line.Intersects(l, out p))
                {
                    V2d d = l.Direction;
                    V2d n = new V2d(-d.Y, d.X);

                    if (!p.IsNaN)
                    {
                        bool addflag = true;
                        bool flag    = direction.Dot(n) > 0;

                        for (int i = 0; i < resulting.Count; i++)
                        {
                            if (Fun.IsTiny((resulting[i] - p).Length))
                            {
                                if (flag != enter[i])
                                {
                                    resulting.RemoveAt(i);
                                    enter.RemoveAt(i);
                                }

                                addflag = false;
                                break;
                            }
                        }

                        if (addflag)
                        {
                            resulting.Add(p);
                            enter.Add(flag);
                        }
                    }
                }
            }


            V2d dir = line.P1 - line.P0;

            resulting = (from r in resulting select r).OrderBy(x => x.Dot(dir)).ToList();

            int           counter = resulting.Count;
            List <Line2d> lines   = new List <Line2d>();

            for (int i = 0; i < counter - 1; i += 2)
            {
                lines.Add(new Line2d(resulting[i], resulting[i + 1]));
            }
            return(lines);
        }
Пример #13
0
        // 2-Dimensional

        #region V2d - V2d

        public static bool IsOrthogonalTo(this V2d u, V2d v)
        {
            return(Fun.IsTiny(u.Dot(v)));
        }
Пример #14
0
 /// <summary>
 /// The signed height of the supplied point over the plane.
 /// </summary>
 public double Height(V2d p) => V2d.Dot(Normal, p) - Distance;
Пример #15
0
 /// <summary>
 /// Gets the point on the ray that is closest to the given point.
 /// Ray direction must be normalized (length 1).
 /// </summary>
 public V2d GetClosestPointOnRay(V2d p)
 {
     return(Origin + Direction * Direction.Dot(p - Origin));
 }